Skip to content

Implement Level 6 (UUID) and Level 7 (REST Update) for IDOR #512#628

Open
anatolii-kandiuk wants to merge 27 commits into
SasanLabs:masterfrom
anatolii-kandiuk:feature/idor-vulnerability
Open

Implement Level 6 (UUID) and Level 7 (REST Update) for IDOR #512#628
anatolii-kandiuk wants to merge 27 commits into
SasanLabs:masterfrom
anatolii-kandiuk:feature/idor-vulnerability

Conversation

@anatolii-kandiuk
Copy link
Copy Markdown

@anatolii-kandiuk anatolii-kandiuk commented May 21, 2026

  • Level 6 (UUID Based IDOR):
    Added uuid column (VARCHAR(36)) to idor_users table in the database schema.
    Created endpoint /LEVEL_6 that blindly trusts the provided UUID parameter, exposing the user profile without authorization checks.

  • Level 7 (REST API Updates / Privilege Escalation):
    Granted UPDATE privileges to the DB user for the idor_users table.
    Created PUT /LEVEL_7 endpoint that accepts a JSON Payload (@RequestBody).
    The server processes the update (e.g., changing salary and role) based on the userId provided in the JSON body, leading to Mass Assignment / Privilege Escalation.

Summary by CodeRabbit

  • New Features

    • Added three IDOR demo levels: Level 6 — retrieve profiles by optional UUID; Level 7 — update a user’s salary/role via PUT by userId; Level 8 — update a user’s salary/role via PUT by UUID (requires UUID in request).
  • Chores

    • Added UUID to user model and seed data (expanded user set); DB schema updated with UUID column and granted UPDATE rights; added related payloads and i18n messages.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a uuid field to User and DB schema/data, expands seed rows, and implements IDOR level6 (UUID fetch), level7 (PUT update by id), and level8 (PUT update by uuid) with SQL/JDBC, tests, payloads, and i18n updates.

Changes

IDOR UUID-based levels and model update

Layer / File(s) Summary
User model with UUID field
src/main/java/org/sasanlabs/service/vulnerability/idor/User.java
User class extended with a uuid field, updated all-args constructor to include uuid, and new getUuid/setUuid accessors.
Database schema and seed data for UUID support
src/main/resources/scripts/IDOR/db/schema.sql, src/main/resources/scripts/IDOR/db/data.sql
idor_users table gains uuid VARCHAR(36), application role granted UPDATE, and seed inserts expanded to include UUID values and additional users.
IDOR service endpoints, SQL constants, and JDBC helpers
src/main/java/org/sasanlabs/service/vulnerability/idor/IDORVulnerability.java, src/main/resources/attackvectors/IDORVulnerabilityPayload.properties, src/main/resources/i18n/messages.properties
Adds level6 (UUID fetch), level7 (PUT update by id), and level8 (PUT update by uuid) handlers; updates SQL constants to include uuid, adds SQL_PROFILE_BY_UUID, SQL_UPDATE_PROFILE_BY_ID, SQL_UPDATE_PROFILE_BY_UUID; implements fetchUserByUuid, updateUserById, updateUserByUuid, and extends fetchAllUsers mapping. Also updates attack payloads and i18n messages.
Test SQL and fixtures
src/test/java/org/sasanlabs/service/vulnerability/idor/IDORVulnerabilityTest.java
Updates SQL_PROFILE_BY_ID test string and multiple mocked User fixtures to the new constructor including uuid.

Sequence Diagram

sequenceDiagram
  participant Client
  participant IDORV as IDORVulnerability
  participant Service as IDORService
  participant DB as Database

  Client->>IDORV: GET/PUT requests for level6/7/8 (cookie tokens)
  IDORV->>Service: fetchUserByUuid / updateUserById / updateUserByUuid
  Service->>DB: SQL_PROFILE_BY_UUID / SQL_UPDATE_PROFILE_BY_ID / SQL_UPDATE_PROFILE_BY_UUID
  DB-->>Service: user row / update count
  Service-->>IDORV: result
  IDORV-->>Client: GenericVulnerabilityResponseBean
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I nibble rows where UUIDs hide,

I fetch and write with a curious stride,
From schema seeds to endpoints new,
Tests hop along to prove the view,
The rabbit cheers at every UUID.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: implementing IDOR level 6 (UUID-based) and level 7 (REST update), matching the core additions across multiple files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/main/resources/scripts/IDOR/db/data.sql (1)

1-3: ⚡ Quick win

Use explicit column lists in seed inserts.

Positional inserts are brittle when schema evolves. Explicit columns make the seed contract stable and self-documenting.

Proposed refactor
-INSERT INTO idor_users VALUES (1, '324bdf11-ef1a-48f0-b342-436779c24456', 'Alice', 'A1B2C3D4E5F60708:141efd4aa55f37300ad1c06d63854eca83a8d41953c5667b9d24eaff4a3edd1d', 50000, 'USER');
-INSERT INTO idor_users VALUES (2, '747e108e-10b1-463e-9328-c453070a1975', 'Bob', '6F1A9D3C7B2E4A10:8944bc8b1b5e87e1d2046a27c8e524c10cf6f6169c2a66cfb19b74513b679307', 60000, 'USER');
-INSERT INTO idor_users VALUES (3, '7b6b8212-66ff-4193-8915-e35399a37945', 'Charlie', 'C4E8B1D9A2F63750:ee5282141fdffb8adeb83550ac95436cdf2e5bee3be1353fcf2b284a03eba13e', 70000, 'ADMIN');
+INSERT INTO idor_users (id, uuid, username, password, salary, role) VALUES (1, '324bdf11-ef1a-48f0-b342-436779c24456', 'Alice', 'A1B2C3D4E5F60708:141efd4aa55f37300ad1c06d63854eca83a8d41953c5667b9d24eaff4a3edd1d', 50000, 'USER');
+INSERT INTO idor_users (id, uuid, username, password, salary, role) VALUES (2, '747e108e-10b1-463e-9328-c453070a1975', 'Bob', '6F1A9D3C7B2E4A10:8944bc8b1b5e87e1d2046a27c8e524c10cf6f6169c2a66cfb19b74513b679307', 60000, 'USER');
+INSERT INTO idor_users (id, uuid, username, password, salary, role) VALUES (3, '7b6b8212-66ff-4193-8915-e35399a37945', 'Charlie', 'C4E8B1D9A2F63750:ee5282141fdffb8adeb83550ac95436cdf2e5bee3be1353fcf2b284a03eba13e', 70000, 'ADMIN');
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/scripts/IDOR/db/data.sql` around lines 1 - 3, The three
INSERT statements into the idor_users table use positional inserts which are
brittle; update each INSERT INTO idor_users VALUES (...) to specify the explicit
column list (e.g., id, uuid, name, password_hash, salary, role) so the seed data
remains stable if the schema changes and is self-documenting; modify the three
INSERTs that currently insert Alice, Bob, and Charlie to include that column
list and keep the same values in the same order.
src/main/resources/scripts/IDOR/db/schema.sql (1)

5-5: ⚡ Quick win

Add a UNIQUE/NOT NULL constraint for idor_users.uuid (schema hardening).

Current Level 6 lookup already takes the first matching row, but the bundled seed data uses distinct, non-empty UUIDs and the app doesn’t write uuid, so the “Level 6 breaks due to duplicates” risk isn’t evidenced here. Still, enforcing NOT NULL UNIQUE prevents invalid/duplicate data from ever being introduced.

Proposed fix
-    uuid VARCHAR(36),
+    uuid VARCHAR(36) NOT NULL UNIQUE,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/scripts/IDOR/db/schema.sql` at line 5, Update the
idor_users.uuid column definition to enforce uniqueness and non-nullability by
changing the column declaration for "uuid" in the idor_users table (the symbol:
idor_users.uuid) from "uuid VARCHAR(36)" to a NOT NULL UNIQUE declaration (e.g.,
VARCHAR(36) NOT NULL UNIQUE or add an explicit UNIQUE CONSTRAINT on uuid) so the
schema prevents duplicate or empty UUIDs from being inserted.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/main/resources/scripts/IDOR/db/data.sql`:
- Around line 1-3: The three INSERT statements into the idor_users table use
positional inserts which are brittle; update each INSERT INTO idor_users VALUES
(...) to specify the explicit column list (e.g., id, uuid, name, password_hash,
salary, role) so the seed data remains stable if the schema changes and is
self-documenting; modify the three INSERTs that currently insert Alice, Bob, and
Charlie to include that column list and keep the same values in the same order.

In `@src/main/resources/scripts/IDOR/db/schema.sql`:
- Line 5: Update the idor_users.uuid column definition to enforce uniqueness and
non-nullability by changing the column declaration for "uuid" in the idor_users
table (the symbol: idor_users.uuid) from "uuid VARCHAR(36)" to a NOT NULL UNIQUE
declaration (e.g., VARCHAR(36) NOT NULL UNIQUE or add an explicit UNIQUE
CONSTRAINT on uuid) so the schema prevents duplicate or empty UUIDs from being
inserted.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 82cd90ef-dfc4-4096-bc57-d3d366af5bef

📥 Commits

Reviewing files that changed from the base of the PR and between 3e4ac50 and 8d09055.

📒 Files selected for processing (4)
  • src/main/java/org/sasanlabs/service/vulnerability/idor/IDORVulnerability.java
  • src/main/java/org/sasanlabs/service/vulnerability/idor/User.java
  • src/main/resources/scripts/IDOR/db/data.sql
  • src/main/resources/scripts/IDOR/db/schema.sql

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/main/java/org/sasanlabs/service/vulnerability/idor/IDORVulnerability.java (3)

265-272: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Run Spotless on this block before merge.

CI is already red on the formatting here. Please run ./gradlew :spotlessApply so spotlessJavaCheck passes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/org/sasanlabs/service/vulnerability/idor/IDORVulnerability.java`
around lines 265 - 272, The annotation block in IDORVulnerability containing
`@AttackVector` and `@VulnerableAppRequestMapping` (for LevelConstants.LEVEL_7 /
IDOR_LEVEL_7_UPDATE) is misformatted; run the project formatter so Spotless
passes (execute ./gradlew :spotlessApply) or apply the Java formatting rules to
this annotation block and re-run spotless checks to ensure spotlessJavaCheck
succeeds before merging.

273-282: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reject incomplete update bodies before persisting them.

This writes salary and role straight from the deserialized body. If either field is absent, a PUT can overwrite the row with default/null values, and the fallback response currently says PLEASE_LOGIN_FIRST even when the target user simply does not exist.

Suggested fix
     public ResponseEntity<GenericVulnerabilityResponseBean<Object>> level7(
         `@RequestBody` User userToUpdate) {
-        
-        int id = userToUpdate.getUserId();
-        
-        if (fetchUserById(id) != null) {
-            updateUser(id, userToUpdate.getSalary(), userToUpdate.getRole());
-            return response(USER_SUCCESSFULLY_UPDATED, true);
-        }
-        return response(PLEASE_LOGIN_FIRST, false);        
+
+        if (userToUpdate == null || userToUpdate.getRole() == null) {
+            return response("Provide userId, salary and role", false, HttpStatus.BAD_REQUEST);
+        }
+
+        int id = userToUpdate.getUserId();
+        if (fetchUserById(id) == null) {
+            return response(USER_NOT_FOUND, false, HttpStatus.NOT_FOUND);
+        }
+
+        updateUser(id, userToUpdate.getSalary(), userToUpdate.getRole());
+        return response(USER_SUCCESSFULLY_UPDATED, true, HttpStatus.OK);
     }

251-262: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Return a UUID-specific validation error here.

LEVEL_6 has no auth gate, so PLEASE_LOGIN_FIRST on a missing uuid is misleading. A parameter-specific error keeps the demo behavior easier to understand.

Suggested fix
     public ResponseEntity<GenericVulnerabilityResponseBean<Object>> level6(
             `@RequestParam`(required = false) String uuid) {
 
         if (uuid != null) {
             User profile = fetchUserByUuid(uuid);
@@
             }
             return response(profile, true);
         }
-        return response(PLEASE_LOGIN_FIRST, false);        
+        return response("Provide uuid", false, HttpStatus.BAD_REQUEST);
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/org/sasanlabs/service/vulnerability/idor/IDORVulnerability.java`
around lines 251 - 262, The controller method level6 currently returns
PLEASE_LOGIN_FIRST when the uuid request param is missing which is misleading;
change the null-uuid branch to return a UUID-specific validation error instead
(e.g., create/use a UUID_REQUIRED or INVALID_UUID constant) by updating the
level6 method to check uuid == null and call response(UUID_REQUIRED, false) (or
an equivalent parameter-specific message), leaving the existing fetchUserByUuid
and USER_NOT_FOUND handling intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@src/main/java/org/sasanlabs/service/vulnerability/idor/IDORVulnerability.java`:
- Around line 265-272: The annotation block in IDORVulnerability containing
`@AttackVector` and `@VulnerableAppRequestMapping` (for LevelConstants.LEVEL_7 /
IDOR_LEVEL_7_UPDATE) is misformatted; run the project formatter so Spotless
passes (execute ./gradlew :spotlessApply) or apply the Java formatting rules to
this annotation block and re-run spotless checks to ensure spotlessJavaCheck
succeeds before merging.
- Around line 251-262: The controller method level6 currently returns
PLEASE_LOGIN_FIRST when the uuid request param is missing which is misleading;
change the null-uuid branch to return a UUID-specific validation error instead
(e.g., create/use a UUID_REQUIRED or INVALID_UUID constant) by updating the
level6 method to check uuid == null and call response(UUID_REQUIRED, false) (or
an equivalent parameter-specific message), leaving the existing fetchUserByUuid
and USER_NOT_FOUND handling intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2eb09f12-4b0a-4673-9a36-c54d84490bae

📥 Commits

Reviewing files that changed from the base of the PR and between 8d09055 and 151a172.

📒 Files selected for processing (1)
  • src/main/java/org/sasanlabs/service/vulnerability/idor/IDORVulnerability.java

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 21, 2026

Codecov Report

❌ Patch coverage is 3.94737% with 73 lines in your changes missing coverage. Please review.
✅ Project coverage is 53.60%. Comparing base (3e4ac50) to head (03c129b).
⚠️ Report is 16 commits behind head on master.

Files with missing lines Patch % Lines
.../service/vulnerability/idor/IDORVulnerability.java 1.78% 54 Missing and 1 partial ⚠️
...ervice/vulnerability/idor/IDORLoginController.java 0.00% 15 Missing ⚠️
...org/sasanlabs/service/vulnerability/idor/User.java 40.00% 3 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master     #628      +/-   ##
============================================
- Coverage     54.78%   53.60%   -1.18%     
- Complexity      660      663       +3     
============================================
  Files            91       91              
  Lines          3565     3662      +97     
  Branches        395      407      +12     
============================================
+ Hits           1953     1963      +10     
- Misses         1435     1522      +87     
  Partials        177      177              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/main/java/org/sasanlabs/service/vulnerability/idor/IDORVulnerability.java (2)

302-323: ⚡ Quick win

Extract the User row mapper and let updateUser return its row count.

fetchUserById, fetchUserByUuid, and fetchAllUsers build the exact same User from the result set — three copies of the same 5-column mapping is easy to drift on the next schema change. A single RowMapper<User> constant (or static helper) keeps them aligned. Likewise, updateUser currently swallows the int row count returned by jdbcTemplate.update, which is exactly what would let level7 distinguish "user not found" from "updated" without the extra SELECT (see the level7 comment).

📝 Suggested refactor
+    private static final org.springframework.jdbc.core.RowMapper<User> USER_ROW_MAPPER =
+            (rs, rowNum) ->
+                    new User(
+                            rs.getInt("id"),
+                            rs.getString("uuid"),
+                            rs.getString("username"),
+                            rs.getInt("salary"),
+                            rs.getString("role"));
+
     private User fetchUserById(int id) {
-        List<User> users =
-                jdbcTemplate.query(
-                        SQL_PROFILE_BY_ID,
-                        new Object[] {id},
-                        (rs, rowNum) ->
-                                new User(
-                                        rs.getInt("id"),
-                                        rs.getString("uuid"),
-                                        rs.getString("username"),
-                                        rs.getInt("salary"),
-                                        rs.getString("role")));
-
-        if (users.isEmpty()) {
-            return null;
-        }
-        return users.get(0);
+        List<User> users = jdbcTemplate.query(SQL_PROFILE_BY_ID, USER_ROW_MAPPER, id);
+        return users.isEmpty() ? null : users.get(0);
     }

     private User fetchUserByUuid(String uuid) {
-        List<User> users =
-                jdbcTemplate.query(
-                        SQL_PROFILE_BY_UUID,
-                        new Object[] {uuid},
-                        (rs, rowNum) ->
-                                new User(
-                                        rs.getInt("id"),
-                                        rs.getString("uuid"),
-                                        rs.getString("username"),
-                                        rs.getInt("salary"),
-                                        rs.getString("role")));
-
-        if (users.isEmpty()) {
-            return null;
-        }
-        return users.get(0);
+        List<User> users = jdbcTemplate.query(SQL_PROFILE_BY_UUID, USER_ROW_MAPPER, uuid);
+        return users.isEmpty() ? null : users.get(0);
     }

-    private void updateUser(int id, int salary, String role) {
-        jdbcTemplate.update(SQL_UPDATE_PROFILE, new Object[] {salary, role, id});
-    }
+    private int updateUser(int id, int salary, String role) {
+        return jdbcTemplate.update(SQL_UPDATE_PROFILE, salary, role, id);
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/org/sasanlabs/service/vulnerability/idor/IDORVulnerability.java`
around lines 302 - 323, Extract a single RowMapper<User> (e.g., USER_ROW_MAPPER)
and replace the duplicated inline mappings in fetchUserById, fetchUserByUuid,
and fetchAllUsers with that constant to ensure a single source of truth for
building User objects; also change updateUser to return the int row count from
jdbcTemplate.update(SQL_UPDATE_PROFILE, new Object[] {salary, role, id}) instead
of void so callers (e.g., level7 logic) can detect "not found" vs "updated", and
update any call sites that expect void to handle the returned count.

249-281: ⚡ Quick win

No unit tests for level6 / level7.

Codecov flags 0% patch coverage on this file (26 missing lines) and IDORVulnerabilityTest doesn't exercise either of the new endpoints. Given the new SQL constants (SQL_PROFILE_BY_UUID, SQL_UPDATE_PROFILE) and the JDBC helpers (fetchUserByUuid, updateUser), please add at least:

  • level6_ShouldReturnUserByUuid — happy path with a matching row.
  • level6_ShouldReturnNotFoundForUnknownUuid — empty JdbcTemplate#query result.
  • level6_ShouldRejectMissingUuiduuid == null branch.
  • level7_ShouldUpdateExistingUser — verifies the jdbcTemplate.update(...) invocation with (salary, role, id).
  • level7_ShouldReturnNotFoundForUnknownIdfetchUserById returns empty.

This also pins the SQL strings and argument order so a future refactor (see updateUser comment) doesn't silently regress.

Want me to draft the test cases above using the existing fixtures pattern in IDORVulnerabilityTest?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/org/sasanlabs/service/vulnerability/idor/IDORVulnerability.java`
around lines 249 - 281, Add unit tests in IDORVulnerabilityTest to cover level6
and level7: implement level6_ShouldReturnUserByUuid (mock
fetchUserByUuid/JdbcTemplate.query to return a User row and assert level6
returns that profile), level6_ShouldReturnNotFoundForUnknownUuid (mock
JdbcTemplate.query to return empty and assert NOT_FOUND response), and
level6_ShouldRejectMissingUuid (call level6 with null and assert
PLEASE_LOGIN_FIRST). For level7 add level7_ShouldUpdateExistingUser (mock
fetchUserById to return a user, call level7 with a User containing
salary/role/id and verify updateUser / jdbcTemplate.update is invoked with
SQL_UPDATE_PROFILE and arguments in order (salary, role, id) and assert
USER_SUCCESSFULLY_UPDATED) and level7_ShouldReturnNotFoundForUnknownId (mock
fetchUserById to return null and assert NOT_FOUND). Use the existing
fixtures/mocking pattern in IDORVulnerabilityTest, reference methods level6,
level7, fetchUserByUuid, fetchUserById, updateUser and constants
SQL_PROFILE_BY_UUID and SQL_UPDATE_PROFILE when verifying SQL and argument
order.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/java/org/sasanlabs/service/vulnerability/idor/IDORVulnerability.java`:
- Around line 249-261: The method level6 currently returns PLEASE_LOGIN_FIRST
when the uuid query param is missing, which is misleading for an unauthenticated
UUID-only endpoint; update level6 to return a more appropriate message (either
reuse USER_NOT_FOUND or introduce a new constant like PROVIDE_UUID) when uuid ==
null, and replace the response(PLEASE_LOGIN_FIRST, false) call accordingly;
ensure the new constant is added to the same response/messages constants class
and used in level6 so responses accurately reflect the validation for
fetchUserByUuid.
- Around line 271-281: In level7 replace the redundant fetchUserById call and
primitive-id pitfalls by validating the incoming id (if id <= 0 return
USER_NOT_FOUND) then call updateUser and branch on the returned row-count: if
rowsUpdated == 0 return USER_NOT_FOUND else return USER_SUCCESSFULLY_UPDATED;
change updateUser to return int (rows affected) and accept boxed Integer salary
and String role (avoid unboxing NPE) and modify SQL_UPDATE_PROFILE to avoid
overwriting values with NULL (use SQL like: SET salary = COALESCE(?, salary),
role = COALESCE(?, role) WHERE id = ?) so omitted salary/role won’t write NULL;
remove fetchUserById usage from level7 and rely on updateUser’s row-count to
determine existence.

---

Nitpick comments:
In
`@src/main/java/org/sasanlabs/service/vulnerability/idor/IDORVulnerability.java`:
- Around line 302-323: Extract a single RowMapper<User> (e.g., USER_ROW_MAPPER)
and replace the duplicated inline mappings in fetchUserById, fetchUserByUuid,
and fetchAllUsers with that constant to ensure a single source of truth for
building User objects; also change updateUser to return the int row count from
jdbcTemplate.update(SQL_UPDATE_PROFILE, new Object[] {salary, role, id}) instead
of void so callers (e.g., level7 logic) can detect "not found" vs "updated", and
update any call sites that expect void to handle the returned count.
- Around line 249-281: Add unit tests in IDORVulnerabilityTest to cover level6
and level7: implement level6_ShouldReturnUserByUuid (mock
fetchUserByUuid/JdbcTemplate.query to return a User row and assert level6
returns that profile), level6_ShouldReturnNotFoundForUnknownUuid (mock
JdbcTemplate.query to return empty and assert NOT_FOUND response), and
level6_ShouldRejectMissingUuid (call level6 with null and assert
PLEASE_LOGIN_FIRST). For level7 add level7_ShouldUpdateExistingUser (mock
fetchUserById to return a user, call level7 with a User containing
salary/role/id and verify updateUser / jdbcTemplate.update is invoked with
SQL_UPDATE_PROFILE and arguments in order (salary, role, id) and assert
USER_SUCCESSFULLY_UPDATED) and level7_ShouldReturnNotFoundForUnknownId (mock
fetchUserById to return null and assert NOT_FOUND). Use the existing
fixtures/mocking pattern in IDORVulnerabilityTest, reference methods level6,
level7, fetchUserByUuid, fetchUserById, updateUser and constants
SQL_PROFILE_BY_UUID and SQL_UPDATE_PROFILE when verifying SQL and argument
order.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e863ff0c-1a91-4241-8770-6cc698cabd8b

📥 Commits

Reviewing files that changed from the base of the PR and between 151a172 and e82a02c.

📒 Files selected for processing (2)
  • src/main/java/org/sasanlabs/service/vulnerability/idor/IDORVulnerability.java
  • src/test/java/org/sasanlabs/service/vulnerability/idor/IDORVulnerabilityTest.java

public ResponseEntity<GenericVulnerabilityResponseBean<Object>> level6(
@RequestParam(required = false) String uuid) {

if (uuid != null) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IDOR vulnerability links with User Login so add login setup as well and after login, user can guess and fetch other users.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


int rowsAffected =
updateUser(
userToUpdate.getUserId(), userToUpdate.getSalary(), userToUpdate.getRole());
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updating existing data is tricky as it will impact existing levels as well. best to not update existing users which are used in other levels. Can you please create 3 new users and update them from this level. Also if possible showcase update for both UUID and ID levels.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/java/org/sasanlabs/service/vulnerability/idor/IDORVulnerability.java`:
- Around line 258-260: Wrap calls to idorLoginService.decodeToken(cookieToken)
in a try-catch so token decode failures do not bubble up as 500s; catch
Exception (or the specific token decode exception), log if needed, set the
validation result to the controlled INVALID_TOKEN response (use the existing
INVALID_TOKEN constant/response handling used in this class) and return/exit the
handler early instead of proceeding (this applies to the decodeToken call near
the cookieToken/null check and the other two occurrences around the uuid checks
at the other noted locations).
- Around line 280-282: The level7 handler (public
ResponseEntity<GenericVulnerabilityResponseBean<Object>> level7(...,
`@RequestBody` User userToUpdate)) currently dereferences userToUpdate without
checking for null; add a defensive null-check at the start of level7 and return
a safe ResponseEntity/GenericVulnerabilityResponseBean indicating a bad request
(or no-op) when userToUpdate is null instead of continuing to access its fields.
Apply the same pattern to the other controller methods that accept `@RequestBody`
User (the other handlers referenced in the review) so each method validates the
request body before any dereference and returns an appropriate error
ResponseEntity when null.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b9295651-d0bd-4577-baec-a4ec31f0f46f

📥 Commits

Reviewing files that changed from the base of the PR and between e82a02c and 103636d.

📒 Files selected for processing (4)
  • src/main/java/org/sasanlabs/service/vulnerability/idor/IDORVulnerability.java
  • src/main/resources/attackvectors/IDORVulnerabilityPayload.properties
  • src/main/resources/i18n/messages.properties
  • src/main/resources/scripts/IDOR/db/data.sql
✅ Files skipped from review due to trivial changes (1)
  • src/main/resources/scripts/IDOR/db/data.sql

Comment thread src/main/java/org/sasanlabs/service/vulnerability/idor/IDORVulnerability.java Outdated
Comment thread src/main/java/org/sasanlabs/service/vulnerability/idor/IDORVulnerability.java Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/main/java/org/sasanlabs/service/vulnerability/idor/IDORVulnerability.java (1)

296-300: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid writing NULL on partial update payloads.

Line 296 and Line 332 pass nullable salary/role straight into the update SQL. A body like {"userId": 4} or {"uuid": "..."} will clear those columns or trip a DB constraint instead of behaving like a targeted update. Preserve existing values with COALESCE(...) in the update queries, or reject requests where both mutable fields are absent.

🛠️ Minimal fix
-    private static final String SQL_UPDATE_PROFILE_BY_ID =
-            "UPDATE idor_users SET salary=?, role=? WHERE id=?";
-    private static final String SQL_UPDATE_PROFILE_BY_UUID =
-            "UPDATE idor_users SET salary=?, role=? WHERE uuid=?";
+    private static final String SQL_UPDATE_PROFILE_BY_ID =
+            "UPDATE idor_users SET salary=COALESCE(?, salary), role=COALESCE(?, role) WHERE id=?";
+    private static final String SQL_UPDATE_PROFILE_BY_UUID =
+            "UPDATE idor_users SET salary=COALESCE(?, salary), role=COALESCE(?, role) WHERE uuid=?";

Also applies to: 332-336

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/org/sasanlabs/service/vulnerability/idor/IDORVulnerability.java`
around lines 296 - 300, The update methods (e.g., updateUserById and
updateUserByUuid) are currently passing nullable salary/role directly into the
UPDATE SQL which will write NULLs when the payload omits those fields; change
the SQL to preserve existing column values by wrapping parameters with
COALESCE(column, ?) or equivalent (e.g., SET salary = COALESCE(?, salary), role
= COALESCE(?, role)) so missing fields do not overwrite current values, and/or
add input validation in the caller (where updateUserById/updateUserByUuid are
invoked) to reject requests that provide neither mutable field before attempting
the update.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In
`@src/main/java/org/sasanlabs/service/vulnerability/idor/IDORVulnerability.java`:
- Around line 296-300: The update methods (e.g., updateUserById and
updateUserByUuid) are currently passing nullable salary/role directly into the
UPDATE SQL which will write NULLs when the payload omits those fields; change
the SQL to preserve existing column values by wrapping parameters with
COALESCE(column, ?) or equivalent (e.g., SET salary = COALESCE(?, salary), role
= COALESCE(?, role)) so missing fields do not overwrite current values, and/or
add input validation in the caller (where updateUserById/updateUserByUuid are
invoked) to reject requests that provide neither mutable field before attempting
the update.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8c37e365-c6eb-4dec-bab7-29d30edc1fa4

📥 Commits

Reviewing files that changed from the base of the PR and between 103636d and adb6622.

📒 Files selected for processing (1)
  • src/main/java/org/sasanlabs/service/vulnerability/idor/IDORVulnerability.java

return jdbcTemplate.update(SQL_UPDATE_PROFILE_BY_ID, new Object[] {salary, role, id});
}

private int updateUserByUuid(String uuid, Integer salary, String role) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think better way is introduce a field in database called Level and update based levels will have there own users which they can manipulate.

}

private int updateUserByUuid(String uuid, Integer salary, String role) {
if ("747e108e-10b1-463e-9328-c453070a1975".equals(uuid)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If checks in code are not good.

payload = "IDOR_PAYLOAD_LEVEL_7")
@VulnerableAppRequestMapping(
value = LevelConstants.LEVEL_7,
htmlTemplate = "LEVEL_7/IDOR",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont see these templates in the code. please add the html templates like it is done for other levels.

Image

Copy link
Copy Markdown
Member

@preetkaran20 preetkaran20 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest testing these code changes as they are not working.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants