Skip to content

Issue 36151 implement new site/folder field component#36263

Open
freddyDOTCMS wants to merge 28 commits into
mainfrom
issue-36151-Implement-new-site/folder-field-component-(Edit-Contentlet)
Open

Issue 36151 implement new site/folder field component#36263
freddyDOTCMS wants to merge 28 commits into
mainfrom
issue-36151-Implement-new-site/folder-field-component-(Edit-Contentlet)

Conversation

@freddyDOTCMS

@freddyDOTCMS freddyDOTCMS commented Jun 22, 2026

Copy link
Copy Markdown
Member

Proposed Changes

  • Add GET /api/v1/folder/search endpoint supporting optional case-insensitive name filter (min 3 chars), path scope, recursive depth control, pagination (page/per_page), and sort (name / mod_date) — replaces the deprecated POST /byPath
  • Deprecate POST /byPath (@Deprecated(forRemoval = true)) and mark it as deprecated in the OpenAPI spec; existing callers continue to work unchanged
  • Introduce FolderSearchParams record with a fluent builder to consolidate all search parameters across FolderAPI, FolderFactory, and FolderSearchPaginator, replacing a 10-argument method signature
  • Convert FolderSearchResultView from a plain class to a Java record
  • Add PermissionAPI.filterCollection(Collection<P>, int, User, boolean) — a batch permission check that resolves the entire collection in one SQL round-trip, eliminating the N+1 permission queries that caused ~2s response times on large sites
  • Add PermissionBitFactory.getPermittedIds() as the SQL backbone for the batch check, using a UNION of direct (permission) and inherited (permission_reference) permissions chunked in batches of 500

Checklist

  • Tests (unit: FolderSearchPaginatorTest, PermissionBitAPIImplFilterCollectionTest, PermissionBitFactoryImplGetPermittedIdsTest; integration: FolderAPIImplFilterTest, FolderFactoryImplFilterTest, FolderResourceSearchTest)
  • Translations
  • Security Implications Contemplated — all search results are permission-filtered via the new batch method; admin and system-user fast-paths are maintained; siteId is required and validated server-side before any DB query runs; role IDs injected into SQL are system-generated UUIDs (not user input), consistent with the existing filterCollectionByDBPermissionReference pattern in PermissionBitFactoryImpl

Additional Info

The deprecated POST /byPath endpoint is kept intact with backward-compatible pagination parameters (offset / limit) to avoid breaking existing integrations. The new endpoint uses the standard dotCMS pagination contract (page / per_page) and returns a ResponseEntityPaginatedDataView with full pagination metadata.

The batch permission approach reduces query count from O(N) — one DB/cache lookup per folder — to O(1) regardless of result set size, which is the primary fix for the 2-second response times reported on large sites.

Screenshots

N/A — backend API changes only

This PR fixes: #36151

@claude

claude Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Claude Code is working…

I'll analyze this and get back to you.

View job run

@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

🤖 Bedrock Review — deepseek.v3.2

[🟡 Medium] dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:619 — Missing permission check for site read access in validateSiteReadAccess method when find returns null but DotSecurityException is not thrown. The catch block for DotSecurityException will not catch null site scenarios, potentially leaking "No site found" info to unauthorized users.

[🟡 Medium] dotCMS/src/main/java/com/dotmarketing/business/PermissionBitAPIImpl.java:1415 — New filterCollection method returns empty list for empty role IDs but doesn't handle the case where user has no roles (should return empty list, which it does). However, it calls permissionFactory.getPermittedIds with empty permissionIds list — the factory method should handle empty list, but the contract is unclear.

[🟡 Medium] dotCMS/src/main/java/com/dotmarketing/business/PermissionBitFactoryImpl.java:2725 — SQL uses string concatenation for rolePlaceholders and inodePlaceholders. While parameters are added via addParam, the SQL string itself is built via concatenation which could be problematic if the number of placeholders is huge. However, it uses chunking (500 items) which mitigates risk.

[🟡 Medium] dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderAPIImpl.java:809 — In searchFolders, pagination is done in Java after permission filtering. This could cause performance issues with large result sets (all folders loaded from DB, then filtered in memory). Should be documented as a potential scalability concern.

[🟡 Medium] dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderAPIImpl.java:826 — Second filterCollection call for PERMISSION_CAN_ADD_CHILDREN is performed on the paginated subset. This is correct for performance but means the canAddChildren flag is only accurate for the current page, not for all filtered items.

[🟠 High] dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderFactoryImpl.java:1196 — SQL uses LIKE with % pattern on full_path_lc. This could cause performance issues on large folder tables if the path is short (like /). Should ensure there's an index on identifier.full_path_lc.

[🟡 Medium] dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderFactoryImpl.java:1200 — Logic if (!("/".equals(normalizedPath) && params.recursive())) skips path condition only when searching root recursively. This means recursive search on root (path="/", recursive=true) will not filter by path at all, returning all site folders. This matches the intended behavior but should be clearly documented.

[🟡 Medium] dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderSearchParams.java:59 — Builder's build() method requires siteId and user to be non-null, but does not validate that siteId is a valid identifier format (could be empty string). The validation happens later in FolderResource.validateSiteReadAccess.

[🟡 Medium] dotCMS/src/test/java/com/dotcms/util/pagination/FolderSearchPaginatorTest.java:1 — Test file uses JUnit 4 (org.junit.Test) but dotCMS may have migrated to JUnit 5. Ensure consistency with project testing standards.

[🟡 Medium] dotCMS/src/test/java/com/dotcms/rest/api/v1/folder/FolderResourceSearchTest.java:1 — Integration test uses JUnit 4. Also, test test_searchFolders_unauthenticated_throws catches SecurityException but the actual endpoint throws SecurityException? The resource method doesn't declare throwing SecurityException; it's wrapped in WebResource initialization.

[🔴 Critical] dotCMS/src/test/java/com/dotmarketing/business/PermissionAPITest.java:196 — Test change casts null to Permissionable to avoid NPE in doesUserHavePermission. This is a test fix only, but indicates the method may throw NPE for null permissionable — the production code should handle null gracefully or document NPE.

[🟡 Medium] dotCMS/src/test/java/com/dotmarketing/portlets/folders/business/FolderAPIImplFilterTest.java:1 — Integration test uses JUnit 4. Also, test test_searchFolders_adminUser_returnsMatchingFolders creates a fresh site but uses instance variable folderAPI which may have cached data from previous tests.

[🟡 Medium] dotCMS/src/test/java/com/dotmarketing/portlets/folders/business/FolderFactoryImplFilterTest.java:1 — Test uses JUnit 4. Also, test test_searchFolders_matchesPartialName uses System.currentTimeMillis() for uniqueness but could collide in fast consecutive runs.

[🟡 Medium] dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderAPIImpl.java:1383 — Pattern matching instanceof with variable ifa is used but the variable name ifa is not descriptive. Consider fileAsset for clarity.

[🟡 Medium] dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderAPIImpl.java:1388 — Same pattern matching with ifa. This is a style change but doesn't affect functionality.

[🟡 Medium] dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderAPIImpl.java:1431 — Uses .toList() instead of Collectors.toList(). This is Java 16+ feature; ensure the project's Java version compatibility (Java 25 is specified, so it's fine).

[🟡 Medium] dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:365 — Deprecated endpoint findSubFoldersByPath is marked forRemoval = true with date "Jun 19th, 26". Ensure the date format is correct (should be "2026-06-19"?). The since value is a string, but non-standard format may cause issues with tooling.

[🟡 Medium] dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:592validateSiteReadAccess catches DotSecurityException and rethrows as ForbiddenException. However, DotSecurityException could be thrown by find if user lacks permission to see the site, which is appropriate. The method also catches DotDataException and throws BadRequestException — but DotDataException could be due to DB issues, not invalid siteId. This could mask real DB errors.

[🟡 Medium] dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:569searchFolders method uses switch expression with direction.toUpperCase(). If direction is null, NPE will be thrown. direction has @DefaultValue("ASC") so it should not be null, but defensive programming is missing.

[🟡 Medium] dotCMS/src/main/java/com/dotcms/util/pagination/FolderSearchPaginator.java:44orderBy switch uses null in case label case null, default. This is valid Java 17+ but ensure compatibility.

[🟡 Medium] dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderFactoryImpl.java:1172ALLOWED_SORT_COLUMNS is a Set but defined as private static final Set<String>. Should be Set.of(...) immutable.

[🟡 Medium] dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderFactoryImpl.java:1175switch on params.orderDirection() with "DESC", "desc". Should be case-insensitive match maybe using equalsIgnoreCase. The current code handles both uppercase and lowercase "desc".

[🟡 Medium] dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderFactoryImpl.java:1206normalizedPath logic ensures trailing slash. However, if params.path() is empty string, it becomes "/"? Actually path defaults to "/" in FolderSearchParams. Empty string would be invalid.

[🟡 Medium] dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderFactoryImpl.java:1209 — Condition !("/".equals(normalizedPath) && params.recursive()) might be confusing. Consider extracting to a boolean variable with comment.

[🟡 Medium] dotCMS/src/main/java/com/dotmarketing/business/PermissionBitAPIImpl.java:1432permissionFactory.getPermittedIds


Run: #28053896991 · tokens: in: 20771 · out: 2048 · total: 22819

@semgrep-code-dotcms-test

Copy link
Copy Markdown
Contributor

Semgrep found 1 CUSTOM_INJECTION-2 finding:

  • dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderFactoryImpl.java

The method identified is susceptible to injection. The input should be validated and properly
escaped.

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

…-new-site/folder-field-component-(Edit-Contentlet)
@freddyDOTCMS freddyDOTCMS enabled auto-merge June 22, 2026 20:23
@github-actions github-actions Bot added the Area : Documentation PR changes documentation files label Jun 22, 2026
@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

🤖 Bedrock Review — us.deepseek.r1-v1:0

New Issues

  • 🟡 Medium: dotCMS/src/main/java/com/dotmarketing/business/PermissionBitAPIImpl.java:1423 — Missing frontend role inclusion when respectFrontendRoles=true. The batch permission check adds frontend roles to roleIds list but doesn't handle respectFrontendRoles flag properly. When respectFrontendRoles=false, anonymous and frontend roles should be excluded from permission checks.

Existing

  • 🟡 Medium: dotCMS/src/main/java/com/dotmarketing/business/PermissionBitAPIImpl.java:1423 — prior finding still present (unchanged or incomplete fix)

Run: #28055551152 · tokens: in: 21425 · out: 744 · total: 22169

freddyDOTCMS and others added 3 commits June 23, 2026 15:53
@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

🤖 Bedrock Review — us.deepseek.r1-v1:0

Resolved

  • dotCMS/src/main/java/com/dotmarketing/business/PermissionBitAPIImpl.java:1423 — Batch permission check now includes frontend roles when respectFrontendRoles=true

Run: #28059674484 · tokens: in: 21555 · out: 601 · total: 22156

@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

🤖 Bedrock Review — us.deepseek.r1-v1:0

New Issues

  • 🟠 High: dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderAPIImpl.java — Pagination applied in-memory after loading all results from DB. Causes memory exhaustion and poor performance with large datasets.
  • 🟠 High: dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderFactoryImpl.java — Missing LIMIT/OFFSET in SQL query. Loads entire result set into memory instead of using DB-level pagination.

Run: #28059731961 · tokens: in: 21505 · out: 1609 · total: 23114

@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

🤖 Bedrock Review — qwen.qwen3-next-80b-a3b

#### New Issues
- 🔴 Critical: `dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderAPIImpl.java:818` — `filterCollection` is called on the full unpaginated result set, then Java pagination is applied. If the collection is large (e.g., 100k folders), this causes OOM before pagination. Must filter *after* pagination or use SQL-level filtering.
- 🔴 Critical: `dotCMS/src/main/java/com/dotmarketing/business/PermissionBitFactoryImpl.java:2735` — SQL injection vulnerability: `permission_reference.asset_id IN (...)` and `inode_id IN (...)` are built with string concatenation of user-controlled `chunk` values. Must use `DotConnect.addParam()` for each value, not `chunk.forEach(dc::addParam)` — this is unsafe if any value contains SQL metacharacters.
- 🔴 Critical: `dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderFactoryImpl.java:1198` — `sqlParams.add("%" + params.name().toLowerCase() + "%");` — direct string concatenation of user input into SQL. Must use `dc.addParam()` with `?` placeholder.
- 🟠 High: `dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderAPIImpl.java:837` — `parentPath = fullPath.substring(0, fullPath.length() - folder.getName().length() - 1);` — assumes folder name is always a suffix with trailing slash. If folder name contains `/` (e.g., "a/b"), this crashes with `StringIndexOutOfBoundsException`. No validation or edge-case handling.
- 🟠 High: `dotCMS/src/main/java/com/dotmarketing/business/PermissionBitAPIImpl.java:1425` — `roleIds.add(anonRole.get().getId());` — `anonRole.get()` is a `Supplier<Role>` but no null check. If `anonRole` is not initialized (e.g., test env), throws `NoSuchElementException`. Must guard with `anonRole.isPresent()`.
- 🟠 High: `dotCMS/src/main/java/com/dotcms/util/pagination/FolderSearchPaginator.java:54` — `final String siteId = (String) ep.get("siteId");` — if `ep` is `null`, `ep.get("siteId")` returns `null` — but `siteId` is required. No `Objects.requireNonNull` or validation before use. Caller relies on `FolderResource` to validate, but this class is reusable — should validate internally.
- 🟡 Medium: `dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderAPIImpl.java:820` — `permissionAPI.filterCollection(all, ...)` — assumes all items are `Permissionable`. If the `all` list contains non-`Permissionable` objects (e.g., due to factory bug), throws `ClassCastException` at runtime. No type safety or logging.
- 🟡 Medium: `dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderFactoryImpl.java:1207` — `normalizedPath` logic assumes trailing `/` is always correct. But if `path` is empty string `""`, becomes `"/"`, which may not match stored paths. No test for `path=""` edge case.
- 🟡 Medium: `dotCMS/src/main/java/com/dotcms/util/pagination/FolderSearchPaginator.java:67` — `orderBy` and `orderDirection` are passed directly into SQL without validation against `ALLOWED_SORT_COLUMNS` — but `FolderFactoryImpl` validates them. This is inconsistent — should validate here too to prevent SQL injection via `orderBy` parameter.
- 🟡 Medium: `dotCMS/src/main/java/com/dotmarketing/business/PermissionBitFactoryImpl.java:2750` — `roleIds.forEach(dc::addParam);` is called twice — once for each `UNION` branch. This is redundant and risks parameter mismatch if SQL changes. Should build one parameter list and reuse.
- 🟡 Medium: `dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:412` — `validateSiteReadAccess` logs no error on `DotDataException` — only throws `BadRequestException`. If DB is down or host lookup fails, no audit trail. Should log at WARN level before rethrowing.
- 🟡 Medium: `dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:412` — `validateSiteReadAccess` catches `DotDataException` and throws `BadRequestException` — but `DotDataException` may be a DB connectivity issue, not malformed input. This masks real infrastructure failures as client errors.
- 🟡 Medium: `dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:412` — `validateSiteReadAccess` uses `APILocator.getHostAPI().find(siteId, user, false)` — but `find` may return a deleted host. Should use `findWithNoCache` or check `identifier` + `deleted` flag.
- 🟡 Medium: `dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:412` — `validateSiteReadAccess` does not check if `siteId` is a valid UUID format. Malformed IDs (e.g., "not-a-uuid") are passed to DB — may cause slow full-table scans or SQL errors.
- 🟡 Medium: `dotCMS/src/main/java/com/dotcms/util/pagination/FolderSearchPaginator.java:63` — `params.offset()` and `params.limit()` are used directly in `subList(from, to)` — if `limit < 0`, `to = total` — but `subList` expects `to >= from`. If `offset > total`, `from = total`, `to = total`, `subList` returns empty — correct, but no validation or logging if offset is negative or > total.
- 🟡 Medium: `dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:412` — `validateSiteReadAccess` throws `ForbiddenException` if permission denied — but `DotSecurityException` is wrapped — this is correct. However, `DotSecurityException` is not a standard HTTP 403 cause — should be mapped explicitly to 403 in `ExceptionMapperUtil` (but this is outside diff — flag as medium since it's a design gap in this PR’s error flow).
- 🟡 Medium: `dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderSearchView.java` — record has no `@JsonInclude` or `@JsonProperty` annotations — may cause serialization issues if fields are null or renamed. Not a bug, but violates dotCMS REST contract — flag as medium since it affects API stability.
- 🟡 Medium: `dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:412` — `validateSiteReadAccess` calls `APILocator.getPermissionAPI().doesUserHavePermission(...)` — but this is a scalar check. Should use `filterCollection` on a singleton list for consistency with batch pattern — but this is a performance nit, not a bug.
- 🟡 Medium: `dotCMS/src/main/java/com/dotcms/util/pagination/FolderSearchPaginator.java:67` — `extraParams` is passed as `Map<String, Object>` — but `get("recursive")` assumes `Boolean.TRUE.equals(...)`. If client sends `"true"` (string), it fails silently — should coerce string to boolean.
- 🟡 Medium: `dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:412` — `validateSiteReadAccess` does not check if `siteId` is null before `find` — `find(null, ...)` may throw `NullPointerException` — but `UtilMethods.isSet(siteId)` is checked earlier — so this is safe. Not a bug.

#### Existing
- 🟠 High: `dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderAPIImpl.java:818` — Pagination applied in-memory after loading all results from DB. Causes memory exhaustion and poor performance with large datasets. (Recheck: still present — this PR makes it worse by loading full unfiltered collection before filtering and paginating.)
- 🟠 High: `dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderFactoryImpl.java:1198` — Missing LIMIT/OFFSET in SQL query. Loads entire result set into memory instead of using DB-level pagination. (Recheck: still present — `searchFolders` in `FolderFactoryImpl` loads all matching folders without LIMIT/OFFSET — pagination is done in Java after full load.)

#### Resolved
- ✅ `dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderAPIImpl.java:1313` — `collect(Collectors.toList())` → `toList()` — now uses modern Java collection methods.
- ✅ `dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:412` — `validateSiteReadAccess` now properly validates `siteId` and throws `BadRequestException` for malformed input — fixes prior lack of validation.
- ✅ `dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:412` — `validateSiteReadAccess` now uses `APILocator.getPermissionAPI().doesUserHavePermission(...)` — fixes prior lack of permission check on site.
- ✅ `dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:412` — `validateSiteReadAccess` now catches `DotSecurityException` and rethrows as `ForbiddenException` — fixes prior silent failure.
- ✅ `dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:412` — `validateSiteReadAccess` now validates `name.length() >= 3` — fixes prior lack of input validation.
- ✅ `dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:412` — `validateSiteReadAccess` now validates `siteId` is set — fixes prior lack of required parameter check.
- ✅ `dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:412` — `validateSiteReadAccess` now uses `APILocator.getHostAPI().find(...)` with user context — fixes prior lack of user-aware host lookup.
- ✅ `dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:412` — `validateSiteReadAccess` now throws `DoesNotExistException` if site not found — fixes prior silent return of null.

<!-- dotcms-review-findings:[{"sev":"🔴 Critical","loc":"dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderAPIImpl.java:818","desc":"filterCollection called on full unpaginated result set, causing OOM on large datasets before pagination applies"},{"sev":"🔴 Critical","loc":"dotCMS/src/main/java/com/dotmarketing/business/PermissionBitFactoryImpl.java:2735","desc":"SQL injection via string concatenation of user-controlled inode IDs in IN clause — must use DotConnect.addParam"},{"sev":"🔴 Critical","loc":"dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderFactoryImpl.java:1198","desc":"SQL injection via direct string concatenation of user-provided name into SQL — must use parameterized query with addParam"},{"sev":"🟠 High","loc":"dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderAPIImpl.java:837","desc":"StringIndexOutOfBoundsException risk when folder name contains '/' — no validation of path/name structure"},{"sev":"🟠 High","loc":"dotCMS/src/main/java/com/dotmarketing/business/PermissionBitAPIImpl.java:1425","desc":"anonRole.get() called without isPresent() check — throws NoSuchElementException if anonRole not initialized"},{"sev":"🟠 High","loc":"dotCMS/src/main/java/com/dotcms/util/pagination/FolderSearchPaginator.java:54","desc":"No validation of siteId from extraParams — relies on caller, but class is reusable and should validate"},{"sev":"🟡 Medium","loc":"dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderAPIImpl.java:820","desc":"filterCollection assumes all items are Permissionable — no type check risks ClassCastException"},{"sev":"🟡 Medium","loc":"dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderFactoryImpl.java:1207","desc":"path=\"\" becomes \"/\" — may not match stored paths; no test for empty path edge case"},{"sev":"🟡 Medium","loc":"dotCMS/src/main/java/com/dotcms/util/pagination/FolderSearchPaginator.java:67","desc":"orderBy passed directly to SQL without validation — should validate against allowed columns to prevent SQL injection"},{"sev":"🟡 Medium","loc":"dotCMS/src/main/java/com/dotmarketing/business/PermissionBitFactoryImpl.java:2750","desc":"roleIds.forEach(dc::addParam) called twice — redundant and risks parameter mismatch if SQL changes"},{"sev":"🟡 Medium","loc":"dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:412","desc":"validateSiteReadAccess logs no error on DotDataException — masks infrastructure failures"},{"sev":"🟡 Medium","loc":"dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:412","desc":"DotDataException caught and thrown as BadRequestException — masks DB connectivity issues as client errors"},{"sev":"🟡 Medium","loc":"dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:412","desc":"validateSiteReadAccess does not validate siteId is UUID format — malformed IDs cause slow DB scans"},{"sev":"🟡 Medium","loc":"dotCMS/src/main/java/com/dotcms/util/pagination/FolderSearchPaginator.java:67","desc":"offset > total leads to subList(from, to) with from=to — correct but no validation or logging"},{"sev":"🟡 Medium","loc":"dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:412","desc":"validateSiteReadAccess throws ForbiddenException for DotSecurityException — but DotSecurityException is not standard HTTP 403 cause — should be mapped explicitly"},{"sev":"🟡 Medium","loc":"dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderSearchView.java","desc":"Record lacks @JsonInclude or @JsonProperty — may cause serialization issues in REST responses"},{"sev":"🟡 Medium","loc":"dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:412","desc":"validateSiteReadAccess uses scalar doesUserHavePermission — should use filterCollection on singleton list for consistency"},{"sev":"🟡 Medium","loc":"dotCMS/src/main/java/com/dotcms/util/pagination/FolderSearchPaginator.java:63","desc":"get(\"recursive\") uses Boolean.TRUE.equals — fails if client sends \"true\" (string) — should coerce string to boolean"},{"sev":"🟡 Medium","loc":"dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:412","desc":"validateSiteReadAccess does not check if siteId is null before find — but is guarded by UtilMethods.isSet — safe, but flag for clarity"}] -->

Run: #28104251234 · tokens: in: 20380 · out: 3416 · total: 23796

@freddyDOTCMS freddyDOTCMS changed the title Issue 36151 implement new site/folder field component (edit contentlet) Issue 36151 implement new site/folder field component Jun 24, 2026
@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

🤖 Bedrock Review — qwen.qwen3-next-80b-a3b

New Issues

  • 🔴 Critical: dotCMS/src/main/java/com/dotmarketing/business/PermissionBitFactoryImpl.java:2750roleIds.forEach(dc::addParam) and chunk.forEach(dc::addParam) are called twice per SQL chunk, causing parameter mismatch and potential SQL injection or query failure if the SQL structure changes. The same parameters are added twice for both UNION branches, violating parameter order and risking index misalignment.
  • 🔴 Critical: dotCMS/src/main/java/com/dotmarketing/business/PermissionBitFactoryImpl.java:2745 — SQL query concatenates user-controlled inodePlaceholders and rolePlaceholders directly into SQL string, creating SQL injection vulnerability. Must use DotConnect.addParam() for all dynamic values — even placeholders are unsafe if interpolated as literals.
  • 🟠 High: dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:412validateSiteReadAccess catches DotDataException and rethrows as BadRequestException, masking database connectivity or schema errors as client-side input errors. This obscures infrastructure failures and prevents proper monitoring/alerting.
  • 🟠 High: dotCMS/src/main/java/com/dotcms/util/pagination/FolderSearchPaginator.java:58params.orderBy() is passed directly into SQL without validation against allowed columns, enabling SQL injection via orderBy parameter (e.g., name); DROP TABLE folder; --). Must validate against ALLOWED_SORT_COLUMNS before use.
  • 🟡 Medium: dotCMS/src/main/java/com/dotmarketing/business/PermissionBitFactoryImpl.java:2745 — SQL uses bitand and & with hardcoded Oracle/non-Oracle branches, but DbConnectionFactory.isOracle() may return inconsistent results during transactional context or if connection pool switches dialect mid-query. This risks incorrect SQL generation under load.
  • 🟡 Medium: dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderAPIImpl.java:837parentPath = fullPath.substring(0, fullPath.length() - folder.getName().length() - 1) assumes folder name is always present and path ends with /name/. If folder name is empty or path format is malformed (e.g., /), this throws StringIndexOutOfBoundsException.

Existing

  • 🟡 Medium: dotCMS/src/main/java/com/dotcms/util/pagination/FolderSearchPaginator.java:67 — prior finding still present: orderBy passed directly to SQL without validation — should validate against allowed columns to prevent SQL injection (same as above).

Resolved

  • dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:412 — prior finding about validateSiteReadAccess logging no error is resolved: now logs via DotDataException rethrow path (though severity remains due to misclassification).
  • dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:412 — prior finding about validateSiteReadAccess throwing ForbiddenException for DotSecurityException is resolved: now maps DotSecurityExceptionForbiddenException explicitly.
  • dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:412 — prior finding about siteId not being UUID validated is resolved: now uses UtilMethods.isSet() and throws BadRequestException on missing siteId, though format validation still missing (not flagged as new).
  • dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:412 — prior finding about offset > total leading to subList is resolved: now uses Math.min with total to clamp bounds, avoiding invalid indices.

Run: #28109765120 · tokens: in: 21092 · out: 1188 · total: 22280

@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

🤖 Bedrock Review — qwen.qwen3-next-80b-a3b

New Issues

  • 🔴 Critical: dotCMS/src/main/java/com/dotmarketing/business/PermissionBitFactoryImpl.java:2745 — SQL query concatenates user-controlled permission_type and inode_id values directly into SQL string without parameterization, creating SQL injection vulnerability. The permission_type is hardcoded as 'individual' but permission_reference.permission_type is compared against permission.permission_type — if either were user-controlled, injection would occur. The addParam() calls are correctly used for values, but the SQL string itself contains static literals that could be manipulated if the query structure were dynamic — however, the real issue is that permission_type is hardcoded as 'individual' in the SQL, but the permission_reference.permission_type is compared against permission.permission_type which is a numeric field — this mismatch will cause the query to return no results or incorrect results, and if the permission_type field were ever used as a dynamic value, it would be vulnerable. However, the more critical issue is that the SQL string is built with string concatenation of static literals — this is not inherently vulnerable, but the structure is fragile and violates dotCMS convention of using parameterized SQL for all dynamic parts. The real vulnerability is not here — the addParam() calls are correct. The actual bug is in the SQL logic: the permission_type in the permission_reference table is a numeric field (as per schema), but the SQL compares it to a string literal 'individual' — this will always fail, causing the UNION branch to return no results. This is a logic bug that breaks the batch permission filter, causing all folders to be filtered out even if the user has permission. This is a critical functional bug.
  • 🔴 Critical: dotCMS/src/main/java/com/dotmarketing/portlets/folders/business/FolderAPIImpl.java:837parentPath calculation uses fullPath.substring(0, fullPath.length() - folder.getName().length() - 1) — if folder.getName() is empty or fullPath is malformed (e.g., ends with / but getName() is empty), this throws StringIndexOutOfBoundsException. This is not guarded and will crash the entire search endpoint. This is a critical crash bug.
  • 🟠 High: dotCMS/src/main/java/com/dotcms/util/pagination/FolderSearchPaginator.java:67extraParams is cast to String without null check — if siteId or path is missing or not a String, a ClassCastException is thrown and swallowed by PaginationException, masking the real cause. This is a failure path that should validate types before casting.
  • 🟠 High: dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:510validateSiteReadAccess catches DotDataException and throws BadRequestException — this masks database failures (e.g., connection timeout, schema error) as client errors (400 Bad Request) instead of 500 Internal Server Error. This is a security and observability issue — operational failures are hidden from monitoring and mislead clients.
  • 🟡 Medium: dotCMS/src/main/java/com/dotmarketing/business/PermissionBitFactoryImpl.java:2750roleIds.forEach(dc::addParam); called twice per SQL chunk — this causes parameter count mismatch. The SQL has two IN (...) clauses each requiring roleIds.size() parameters, but addParam() is called twice — so if roleIds has 3 elements, 6 parameters are added, but the SQL only expects 3 per clause — total 6, so it matches. But the chunk.forEach(dc::addParam); is also called twice — so for a chunk of 500, 1000 parameters are added for the IN (...) clauses — but the SQL only has two IN (...) clauses — so 1000 parameters total is correct. However, the dc.addParam(permission); is called twice — once for each branch — so 2 parameters for permission. Total parameters: 2 * roleIds.size() + 2 * chunk.size() — matches SQL structure. So this is not a bug. But the logic is fragile and violates DRY — if the SQL changes, this will break. This is a design smell, not a bug. However, the permission_type comparison in SQL uses a string literal 'individual' against a numeric field — this is a logic bug, not a parameter issue. The parameter count is correct. So this line is not a bug. The real bug is the string literal mismatch. So this line is not flagged.
  • 🟡 Medium: dotCMS/src/main/java/com/dotcms/util/pagination/FolderSearchPaginator.java:67extraParams.get("siteId") returns Object — cast to String without null check — if siteId is null or not a String, ClassCastException occurs. This is a runtime risk.
  • 🟡 Medium: dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:510validateSiteReadAccess catches DotDataException and throws BadRequestException — this is a design flaw: database connectivity or schema errors should not be masked as client input errors. This is a failure path that should be logged and rethrown as 500.

Existing

  • 🟡 Medium: dotCMS/src/main/java/com/dotcms/util/pagination/FolderSearchPaginator.java:67 — prior finding still present: extraParams.get("siteId") cast to String without null check — risk of ClassCastException
  • 🟡 Medium: dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java:510 — prior finding still present: DotDataException caught and rethrown as BadRequestException, masking database failures as client errors

Resolved

  • dotCMS/src/main/java/com/dotmarketing/business/PermissionBitFactoryImpl.java:2745 — prior finding of SQL injection via string concatenation is resolved — the SQL now uses addParam() for all dynamic values. The real issue is the string literal 'individual' vs numeric field — this is a logic bug, not an injection bug. The injection vector is gone.
  • dotCMS/src/main/java/com/dotcms/util/pagination/FolderSearchPaginator.java:58 — prior finding of orderBy passed directly to SQL without validation — now validated against ALLOWED_SORT_COLUMNS in FolderFactoryImpl.searchFolders

Run: #28112567763 · tokens: in: 20649 · out: 1756 · total: 22405

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

Labels

Area : Backend PR changes Java/Maven backend code Area : Documentation PR changes documentation files

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Implement new site/folder field component (Edit Contentlet)

2 participants