ira-review 1.2.1 → 2.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 CHANGED
@@ -1,22 +1,21 @@
1
- IRA Review - Proprietary License
1
+ MIT License
2
2
 
3
- Copyright (c) 2025-present Mayur Patil. All rights reserved.
3
+ Copyright (c) 2024-present Mayur Patil (patilmayur5572@gmail.com)
4
4
 
5
- This software and associated documentation files (the "Software") are the
6
- proprietary property of Mayur Patil.
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:
7
11
 
8
- 1. The CLI tool ("ira-review" npm package) may be used freely for personal
9
- and commercial purposes.
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
10
14
 
11
- 2. You may not copy, modify, distribute, sell, sublicense, or create
12
- derivative works based on the Software or its source code without prior
13
- written permission.
14
-
15
- 3. Pro features in the VS Code extension require a valid license key.
16
- Circumventing license validation is prohibited.
17
-
18
- 4. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
- IMPLIED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
- OTHER LIABILITY ARISING FROM THE USE OF THE SOFTWARE.
21
-
22
- For licensing inquiries: patilmayur5572@gmail.com
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/NOTICE CHANGED
@@ -1,34 +1,7 @@
1
1
  IRA — Intelligent Review Assistant
2
2
  Copyright (c) 2024-present Mayur Patil (patilmayur5572@gmail.com)
3
3
 
4
- This software is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
5
-
6
- === AGPL-3.0 OBLIGATIONS ===
7
-
8
- By using, modifying, or distributing this software, you agree to the following:
9
-
10
- 1. SOURCE CODE DISCLOSURE
11
- If you modify this software or use it as part of a network service,
12
- you MUST make the complete source code of your modified version available
13
- to all users under the AGPL-3.0 license.
14
-
15
- 2. LICENSE PRESERVATION
16
- You must retain all copyright notices, this NOTICE file, and the full
17
- AGPL-3.0 license text in any copies or derivative works.
18
-
19
- 3. NETWORK USE IS DISTRIBUTION
20
- Under AGPL-3.0, providing this software as a network service (e.g., SaaS,
21
- CI/CD pipeline service) counts as distribution. You must offer the source
22
- code to all users who interact with the service.
23
-
24
- 4. NO PROPRIETARY USE WITHOUT COMMERCIAL LICENSE
25
- Using this software in proprietary/closed-source projects without releasing
26
- your source code under AGPL-3.0 requires a commercial license.
27
-
28
- === COMMERCIAL LICENSING ===
29
-
30
- For commercial licensing (use IRA in proprietary projects without AGPL obligations),
31
- contact: patilmayur5572@gmail.com
4
+ This software is licensed under the MIT License. See LICENSE file for details.
32
5
 
33
6
  === THIRD-PARTY NOTICES ===
34
7
 
package/README.github.md CHANGED
@@ -214,7 +214,7 @@ IRA is not a SaaS product. There is no hosted service, no telemetry, no analytic
214
214
  | **Auth** | Environment variables or CLI flags | VS Code OAuth + OS keychain |
215
215
  | **Output** | Terminal + PR comments | Inline diagnostics, CodeLens, TreeView, risk badge |
216
216
  | **JIRA/Sonar** | CLI flags or env vars | VS Code settings |
217
- | **Pro features** | Not applicable | Auto-review on save, one-click fix, history/trends |
217
+ | **Extension features** | Not applicable | Auto-review on save, one-click fix, history/trends |
218
218
 
219
219
  Both surfaces use the same core review engine. The review logic, risk scoring, and AI prompts are identical.
220
220
 
@@ -425,7 +425,7 @@ CLI flags override environment variables, which override the config file. Token
425
425
 
426
426
  ## License
427
427
 
428
- [Proprietary](LICENSE). See LICENSE file for details.
428
+ [MIT](LICENSE)
429
429
 
430
430
  Full CLI reference: `npx ira-review review --help`
431
431
 
package/README.md CHANGED
@@ -15,28 +15,66 @@ No install required. Drop `--dry-run` to post comments directly on the PR. For B
15
15
  ## What You Get
16
16
 
17
17
  ```
18
- IRA: Found 3 issues (Risk: MEDIUM - 47/100)
19
-
20
- src/routes/todos.ts
21
- [BLOCKER] SQL injection risk - user input passed directly to query
22
- [MAJOR] Missing database index on frequently queried column
23
-
24
- src/middleware/auth.ts
25
- [CRITICAL] JWT secret hardcoded - move to environment variable
26
-
27
- JIRA AC Validation (PROJ-1234): # when --jira-ticket is provided
28
- [PASS] User can create a todo item
29
- [FAIL] Input is validated before save
30
- [PASS] Error returns 422 with details
18
+ 🔍 IRA Scanning PR before your reviewers do
19
+
20
+ ✓ Config loaded — AI-only mode, openai, PR #42
21
+ Diff loaded 4 files changed
22
+ Review complete 3 issues found
23
+
24
+ ────────────────────────────────────────────────────────────
25
+ 📄 src/routes/auth.ts:31
26
+ Rule: IRA/security (CRITICAL)
27
+ Message: User input passed directly to SQL query
28
+ Explain: The username parameter is concatenated into a SQL string,
29
+ creating a SQL injection vector.
30
+ Impact: Attacker could execute arbitrary SQL and gain database control.
31
+ Fix: BEFORE: `db.query(`SELECT * FROM users WHERE name = ${username}`)`
32
+ → AFTER: `db.query('SELECT * FROM users WHERE name = $1', [username])`
33
+
34
+ ────────────────────────────────────────────────────────────
35
+ 📄 src/middleware/cors.ts:8
36
+ Rule: IRA/error-handling (MAJOR)
37
+ Message: Empty catch block swallows CORS validation errors
38
+ Explain: fetch() failure in CORS preflight is caught and ignored,
39
+ leaving the request in an undefined state.
40
+ Impact: Silent CORS failures in production with no logging.
41
+ Fix: BEFORE: `} catch {}`
42
+ → AFTER: `} catch (err) { logger.error('CORS preflight failed', err); throw err; }`
43
+
44
+ # 🔍 IRA Review Summary
45
+
46
+ ## 🟡 Risk: MEDIUM (38/100)
47
+
48
+ | Metric | Value |
49
+ |---------------|----------|
50
+ | Review mode | AI-only |
51
+ | Total issues | 3 |
52
+ | Reviewed (AI) | 3 |
53
+ | Framework | react |
54
+
55
+ ## ✅ Requirements: AUTH-234 — 83% Complete (5/6)
56
+
57
+ ✅ OAuth2 login flow implemented with Google provider
58
+ ✅ JWT tokens generated on successful authentication
59
+ ✅ Refresh token rotation with 7-day expiry
60
+ ❌ Input validation on login endpoint — no email format check
61
+ ✅ Logout endpoint clears session and revokes token
62
+ ✅ Rate limiting on login attempts
63
+
64
+ ⚠️ Edge Cases Not Covered
65
+ - What happens when Google OAuth is unreachable?
66
+ - Token refresh during concurrent requests?
31
67
  ```
32
68
 
33
- Each issue is posted as an inline comment on the exact PR line with explanation, impact, and suggested fix.
69
+ Each issue is posted as an inline comment on the exact PR line with explanation, impact, and a minimal BEFORE → AFTER fix.
34
70
 
35
71
  **Features:**
36
72
 
73
+ - Evidence-based reviews — 7 categories (security, business logic, race conditions, data consistency, async, error handling, defensive coding), each with explicit false-positive exclusions. Issues without concrete evidence are filtered out.
37
74
  - Risk scoring (0-100) with severity breakdown and PR labels
38
- - Inline AI comments with explanation, impact, and suggested fix
39
- - JIRA acceptance criteria validation with per-criterion pass/fail
75
+ - Inline AI comments with explanation, impact, and minimal BEFORE → AFTER fix
76
+ - JIRA acceptance criteria validation with per-criterion pass/fail and edge case detection
77
+ - JIRA AC auto-detection — finds AC from custom field or description automatically
40
78
  - Custom team review rules via `.ira-rules.json` (see below)
41
79
  - Test case generation from JIRA tickets (Jest, Vitest, Playwright, etc.)
42
80
  - Comment deduplication across re-runs
@@ -53,6 +91,8 @@ Commit a `.ira-rules.json` to your repo root. Rules are injected into the AI pro
53
91
  "rules": [
54
92
  {
55
93
  "message": "Use parameterized queries for all SQL operations",
94
+ "bad": "db.query(`SELECT * FROM users WHERE id = ${userId}`)",
95
+ "good": "db.query('SELECT * FROM users WHERE id = $1', [userId])",
56
96
  "severity": "CRITICAL",
57
97
  "paths": ["src/db/**", "src/api/**"]
58
98
  },
@@ -62,16 +102,27 @@ Commit a `.ira-rules.json` to your repo root. Rules are injected into the AI pro
62
102
  "good": "logger.info('User created', { userId: user.id });",
63
103
  "severity": "MINOR"
64
104
  }
105
+ ],
106
+ "sensitiveAreas": [
107
+ "src/services/payment/**",
108
+ "**/auth/**",
109
+ "src/config/database.*"
65
110
  ]
66
111
  }
67
112
  ```
68
113
 
114
+ **Rules:**
69
115
  - `message` + `severity` required. `bad`/`good` examples and `paths` are optional.
70
116
  - Rules without `paths` apply to all files. Rules with `paths` match only those directories.
71
- - Maximum 30 rules. Deterministic checks (naming, formatting) belong in ESLint.
117
+ - Maximum 50 rules. Deterministic checks (naming, formatting) belong in ESLint.
72
118
  - Invalid rules are skipped with a warning, not a crash.
73
119
  - No license gating. Works in CLI, CI/CD, and VS Code extension.
74
120
 
121
+ **Sensitive Areas:**
122
+ - Files matching a sensitive area glob get extra scrutiny during review and Apply Fix.
123
+ - Labels are derived from the glob automatically (`src/services/payment/**` → "payment").
124
+ - Sensitive file findings get a higher weight in risk scoring.
125
+
75
126
  ---
76
127
 
77
128
  ## Use Cases
@@ -170,7 +221,7 @@ Tokens are read from environment variables or CLI flags at runtime. Nothing is w
170
221
 
171
222
  ## License
172
223
 
173
- [Proprietary](LICENSE)
224
+ [MIT](LICENSE)
174
225
 
175
226
  ---
176
227
 
package/README.npm.md CHANGED
@@ -15,28 +15,66 @@ No install required. Drop `--dry-run` to post comments directly on the PR. For B
15
15
  ## What You Get
16
16
 
17
17
  ```
18
- IRA: Found 3 issues (Risk: MEDIUM - 47/100)
19
-
20
- src/routes/todos.ts
21
- [BLOCKER] SQL injection risk - user input passed directly to query
22
- [MAJOR] Missing database index on frequently queried column
23
-
24
- src/middleware/auth.ts
25
- [CRITICAL] JWT secret hardcoded - move to environment variable
26
-
27
- JIRA AC Validation (PROJ-1234): # when --jira-ticket is provided
28
- [PASS] User can create a todo item
29
- [FAIL] Input is validated before save
30
- [PASS] Error returns 422 with details
18
+ 🔍 IRA Scanning PR before your reviewers do
19
+
20
+ ✓ Config loaded — AI-only mode, openai, PR #42
21
+ Diff loaded 4 files changed
22
+ Review complete 3 issues found
23
+
24
+ ────────────────────────────────────────────────────────────
25
+ 📄 src/routes/auth.ts:31
26
+ Rule: IRA/security (CRITICAL)
27
+ Message: User input passed directly to SQL query
28
+ Explain: The username parameter is concatenated into a SQL string,
29
+ creating a SQL injection vector.
30
+ Impact: Attacker could execute arbitrary SQL and gain database control.
31
+ Fix: BEFORE: `db.query(`SELECT * FROM users WHERE name = ${username}`)`
32
+ → AFTER: `db.query('SELECT * FROM users WHERE name = $1', [username])`
33
+
34
+ ────────────────────────────────────────────────────────────
35
+ 📄 src/middleware/cors.ts:8
36
+ Rule: IRA/error-handling (MAJOR)
37
+ Message: Empty catch block swallows CORS validation errors
38
+ Explain: fetch() failure in CORS preflight is caught and ignored,
39
+ leaving the request in an undefined state.
40
+ Impact: Silent CORS failures in production with no logging.
41
+ Fix: BEFORE: `} catch {}`
42
+ → AFTER: `} catch (err) { logger.error('CORS preflight failed', err); throw err; }`
43
+
44
+ # 🔍 IRA Review Summary
45
+
46
+ ## 🟡 Risk: MEDIUM (38/100)
47
+
48
+ | Metric | Value |
49
+ |---------------|----------|
50
+ | Review mode | AI-only |
51
+ | Total issues | 3 |
52
+ | Reviewed (AI) | 3 |
53
+ | Framework | react |
54
+
55
+ ## ✅ Requirements: AUTH-234 — 83% Complete (5/6)
56
+
57
+ ✅ OAuth2 login flow implemented with Google provider
58
+ ✅ JWT tokens generated on successful authentication
59
+ ✅ Refresh token rotation with 7-day expiry
60
+ ❌ Input validation on login endpoint — no email format check
61
+ ✅ Logout endpoint clears session and revokes token
62
+ ✅ Rate limiting on login attempts
63
+
64
+ ⚠️ Edge Cases Not Covered
65
+ - What happens when Google OAuth is unreachable?
66
+ - Token refresh during concurrent requests?
31
67
  ```
32
68
 
33
- Each issue is posted as an inline comment on the exact PR line with explanation, impact, and suggested fix.
69
+ Each issue is posted as an inline comment on the exact PR line with explanation, impact, and a minimal BEFORE → AFTER fix.
34
70
 
35
71
  **Features:**
36
72
 
73
+ - Evidence-based reviews — 7 categories (security, business logic, race conditions, data consistency, async, error handling, defensive coding), each with explicit false-positive exclusions. Issues without concrete evidence are filtered out.
37
74
  - Risk scoring (0-100) with severity breakdown and PR labels
38
- - Inline AI comments with explanation, impact, and suggested fix
39
- - JIRA acceptance criteria validation with per-criterion pass/fail
75
+ - Inline AI comments with explanation, impact, and minimal BEFORE → AFTER fix
76
+ - JIRA acceptance criteria validation with per-criterion pass/fail and edge case detection
77
+ - JIRA AC auto-detection — finds AC from custom field or description automatically
40
78
  - Custom team review rules via `.ira-rules.json` (see below)
41
79
  - Test case generation from JIRA tickets (Jest, Vitest, Playwright, etc.)
42
80
  - Comment deduplication across re-runs
@@ -53,6 +91,8 @@ Commit a `.ira-rules.json` to your repo root. Rules are injected into the AI pro
53
91
  "rules": [
54
92
  {
55
93
  "message": "Use parameterized queries for all SQL operations",
94
+ "bad": "db.query(`SELECT * FROM users WHERE id = ${userId}`)",
95
+ "good": "db.query('SELECT * FROM users WHERE id = $1', [userId])",
56
96
  "severity": "CRITICAL",
57
97
  "paths": ["src/db/**", "src/api/**"]
58
98
  },
@@ -62,16 +102,27 @@ Commit a `.ira-rules.json` to your repo root. Rules are injected into the AI pro
62
102
  "good": "logger.info('User created', { userId: user.id });",
63
103
  "severity": "MINOR"
64
104
  }
105
+ ],
106
+ "sensitiveAreas": [
107
+ "src/services/payment/**",
108
+ "**/auth/**",
109
+ "src/config/database.*"
65
110
  ]
66
111
  }
67
112
  ```
68
113
 
114
+ **Rules:**
69
115
  - `message` + `severity` required. `bad`/`good` examples and `paths` are optional.
70
116
  - Rules without `paths` apply to all files. Rules with `paths` match only those directories.
71
- - Maximum 30 rules. Deterministic checks (naming, formatting) belong in ESLint.
117
+ - Maximum 50 rules. Deterministic checks (naming, formatting) belong in ESLint.
72
118
  - Invalid rules are skipped with a warning, not a crash.
73
119
  - No license gating. Works in CLI, CI/CD, and VS Code extension.
74
120
 
121
+ **Sensitive Areas:**
122
+ - Files matching a sensitive area glob get extra scrutiny during review and Apply Fix.
123
+ - Labels are derived from the glob automatically (`src/services/payment/**` → "payment").
124
+ - Sensitive file findings get a higher weight in risk scoring.
125
+
75
126
  ---
76
127
 
77
128
  ## Use Cases
@@ -170,7 +221,7 @@ Tokens are read from environment variables or CLI flags at runtime. Nothing is w
170
221
 
171
222
  ## License
172
223
 
173
- [Proprietary](LICENSE)
224
+ [MIT](LICENSE)
174
225
 
175
226
  ---
176
227
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  BitbucketClient
4
- } from "./chunk-6G34PNVI.js";
4
+ } from "./chunk-XOR6EFZE.js";
5
5
  import "./chunk-AHCFDKK4.js";
6
6
  export {
7
7
  BitbucketClient
@@ -54,6 +54,21 @@ var GitHubClient = class {
54
54
  }
55
55
  });
56
56
  }
57
+ async getPRState(pullRequestId) {
58
+ const url = `${this.baseUrl}/repos/${this.owner}/${this.repo}/pulls/${pullRequestId}`;
59
+ const response = await fetchWithTimeout(url, { headers: this.headers });
60
+ if (response.status === 404) {
61
+ throw new Error(
62
+ `PR #${pullRequestId} was not found \u2014 it may have been deleted.
63
+ \u{1F4A1} Double-check the PR number and try again.`
64
+ );
65
+ }
66
+ if (!response.ok) return "unknown";
67
+ const data = await response.json();
68
+ if (data.merged) return "merged";
69
+ if (data.state === "closed") return "closed";
70
+ return "open";
71
+ }
57
72
  async getDiff(pullRequestId) {
58
73
  const url = `${this.baseUrl}/repos/${this.owner}/${this.repo}/pulls/${pullRequestId}`;
59
74
  return withRetry(async () => {
@@ -73,6 +88,32 @@ var GitHubClient = class {
73
88
  return response.text();
74
89
  });
75
90
  }
91
+ async getDiffPerFile(pullRequestId) {
92
+ const fileMap = /* @__PURE__ */ new Map();
93
+ let page = 1;
94
+ while (true) {
95
+ const url = `${this.baseUrl}/repos/${this.owner}/${this.repo}/pulls/${pullRequestId}/files?per_page=100&page=${page}`;
96
+ const data = await withRetry(async () => {
97
+ const response = await fetchWithTimeout(url, { headers: this.headers });
98
+ if (!response.ok) {
99
+ const text = await response.text();
100
+ throw new RetryableError(`GitHub API error (${response.status}): ${text}`, response.status);
101
+ }
102
+ return response.json();
103
+ });
104
+ for (const file of data) {
105
+ if (file.status === "removed" || !file.patch) continue;
106
+ const unified = `diff --git a/${file.filename} b/${file.filename}
107
+ --- a/${file.filename}
108
+ +++ b/${file.filename}
109
+ ${file.patch}`;
110
+ fileMap.set(file.filename, unified);
111
+ }
112
+ if (data.length < 100) break;
113
+ page++;
114
+ }
115
+ return fileMap;
116
+ }
76
117
  async getFileContent(filePath, pullRequestId) {
77
118
  const sha = await this.getHeadSha(pullRequestId);
78
119
  const encodedPath = filePath.split("/").map(encodeURIComponent).join("/");
@@ -106,6 +106,22 @@ var BitbucketClient = class {
106
106
  return response.text();
107
107
  });
108
108
  }
109
+ async getPRState(pullRequestId) {
110
+ const url = `${this.baseUrl}/repositories/${this.workspace}/${this.repoSlug}/pullrequests/${pullRequestId}`;
111
+ const response = await fetchWithTimeout(url, { headers: this.headers });
112
+ if (response.status === 404) {
113
+ throw new Error(
114
+ `PR #${pullRequestId} was not found \u2014 it may have been deleted.
115
+ \u{1F4A1} Double-check the PR number and try again.`
116
+ );
117
+ }
118
+ if (!response.ok) return "unknown";
119
+ const data = await response.json();
120
+ const state = data.state?.toUpperCase();
121
+ if (state === "MERGED") return "merged";
122
+ if (state === "DECLINED" || state === "SUPERSEDED") return "declined";
123
+ return "open";
124
+ }
109
125
  async getDiff(pullRequestId) {
110
126
  const url = `${this.baseUrl}/repositories/${this.workspace}/${this.repoSlug}/pullrequests/${pullRequestId}/diff`;
111
127
  return withRetry(async () => {
@@ -122,6 +138,46 @@ var BitbucketClient = class {
122
138
  return response.text();
123
139
  });
124
140
  }
141
+ async getDiffPerFile(pullRequestId) {
142
+ const fileMap = /* @__PURE__ */ new Map();
143
+ let nextUrl = `${this.baseUrl}/repositories/${this.workspace}/${this.repoSlug}/pullrequests/${pullRequestId}/diffstat?pagelen=100`;
144
+ const changedFiles = [];
145
+ while (nextUrl) {
146
+ const data = await withRetry(async () => {
147
+ const response = await fetchWithTimeout(nextUrl, { headers: this.headers });
148
+ if (!response.ok) {
149
+ const text = await response.text();
150
+ throw new RetryableError(`Bitbucket API error (${response.status}): ${text}`, response.status);
151
+ }
152
+ return response.json();
153
+ });
154
+ for (const entry of data.values) {
155
+ if (entry.status === "removed") continue;
156
+ const path = entry.new?.path ?? entry.old?.path;
157
+ if (path) changedFiles.push(path);
158
+ }
159
+ nextUrl = data.next ?? null;
160
+ }
161
+ for (const filePath of changedFiles) {
162
+ try {
163
+ const encodedPath = filePath.split("/").map(encodeURIComponent).join("/");
164
+ const url = `${this.baseUrl}/repositories/${this.workspace}/${this.repoSlug}/pullrequests/${pullRequestId}/diff?path=${encodedPath}`;
165
+ const diff = await withRetry(async () => {
166
+ const response = await fetchWithTimeout(url, { headers: this.headers }, 15e3);
167
+ if (!response.ok) {
168
+ const text = await response.text();
169
+ throw new RetryableError(`Bitbucket API error (${response.status}): ${text}`, response.status);
170
+ }
171
+ return response.text();
172
+ });
173
+ if (diff.trim()) {
174
+ fileMap.set(filePath, diff);
175
+ }
176
+ } catch {
177
+ }
178
+ }
179
+ return fileMap;
180
+ }
125
181
  getSourceHash(pullRequestId) {
126
182
  const cached = this.shaCache.get(pullRequestId);
127
183
  if (cached) return cached;