Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1201,7 +1201,8 @@ public void setBinary(com.dotcms.contenttype.model.field.Field field, File newFi
* @throws IOException
*/
public java.io.File getBinary(String velocityVarName)throws IOException {
File f = (File) map.get(velocityVarName);
final Object rawValue = map.get(velocityVarName);
File f = (rawValue instanceof File) ? (File) rawValue : null;
if((f==null || !f.exists()) ){
f=null;
map.remove(velocityVarName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ protected Map<String, Object> transform(final Contentlet contentlet,
final String sufix = options.contains(AVOID_MAP_SUFFIX_FOR_VIEWS)
? "" : "Map";

map.put(field.variable() + sufix, transform(field, contentlet));
final Map<String, Object> binaryMap = transform(field, contentlet);
if (!binaryMap.isEmpty()) {
map.put(field.variable() + sufix, binaryMap);
}
final Metadata metadata = contentlet.getBinaryMetadata(field.variable());
if (!options.contains(AVOID_MAP_SUFFIX_FOR_VIEWS) && metadata != null) {
//This clearly replaces the binary by a string which is the expected output on BinaryToMapTransformer.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
import org.mockito.MockedStatic;
import org.mockito.Mockito;

import java.util.Collections;
import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

public class ContentletTest {

Expand Down Expand Up @@ -106,6 +108,32 @@ public void testGetName_WhenHostTypeHasAliasesListedBeforeHostName_ShouldReturnH
}
}

/**
* Regression test for <a href="https://github.com/dotCMS/core/issues/35188">Issue #35188</a>.
* <p>
* Before the fix, {@code BinaryViewStrategy} wrote {@code Collections.emptyMap()} into the
* contentlet's backing map under the bare field variable key for empty binary fields. A
* subsequent call to {@link Contentlet#getBinary} would then try to cast that {@code Map} to
* {@code File}, throwing a {@code ClassCastException} (surfaced as
* {@code DotDataException: null}).
* <p>
* After the fix, {@code getBinary} uses an {@code instanceof} guard and returns {@code null}
* for any non-{@code File} value rather than throwing.
*/
@Test
public void testGetBinary_whenMapContainsNonFileValue_returnsNullWithoutException()
throws Exception {

final Contentlet contentlet = new Contentlet();
// Simulate the map-poisoning that BinaryViewStrategy caused before the fix.
contentlet.getMap().put("productImage", Collections.emptyMap());

final java.io.File result = contentlet.getBinary("productImage");

assertNull("getBinary() must return null — not throw ClassCastException — "
+ "when the backing map holds a non-File value for the field key", result);
}

private Field createFieldWithVarname(final String varname) {
return ImmutableTextField.builder()
.name(varname)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.dotmarketing.portlets.contentlet.transform.strategy;

import static com.dotmarketing.portlets.contentlet.transform.strategy.TransformOptions.AVOID_MAP_SUFFIX_FOR_VIEWS;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import com.dotcms.api.APIProvider;
import com.dotcms.contenttype.model.field.BinaryField;
import com.dotcms.contenttype.model.field.Field;
import com.dotcms.contenttype.model.type.ContentType;
import com.dotmarketing.portlets.contentlet.model.Contentlet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.Test;
import org.mockito.Mockito;

public class BinaryViewStrategyTest {

/**
* Regression test for <a href="https://github.com/dotCMS/core/issues/35188">Issue #35188</a>.
* <p>
* When a binary field has no uploaded file (metadata is {@code null}), the instance
* {@code transform} method must NOT write {@code emptyMap()} into the output map under the
* bare field variable key (the key used when {@code AVOID_MAP_SUFFIX_FOR_VIEWS} is set).
* <p>
* Before the fix, {@code emptyMap()} was unconditionally placed in the contentlet's backing
* map, poisoning the field slot. A subsequent {@code DefaultTransformStrategy.addBinaries}
* call would then invoke {@code Contentlet.getBinary()}, which tried to cast the
* {@code emptyMap()} to {@code File}, throwing a {@code ClassCastException} wrapped as
* {@code DotDataException: null}.
*/
@Test
public void testTransform_whenBinaryMetadataIsNull_doesNotPutEmptyMapIntoOutputMap()
throws Exception {

final APIProvider toolBox = Mockito.mock(APIProvider.class);
final BinaryViewStrategy strategy = new BinaryViewStrategy(toolBox);

final Field field = Mockito.mock(BinaryField.class);
Mockito.when(field.variable()).thenReturn("productImage");

final ContentType contentType = Mockito.mock(ContentType.class);
Mockito.when(contentType.fields(BinaryField.class)).thenReturn(List.of(field));

final Contentlet contentlet = Mockito.mock(Contentlet.class);
Mockito.when(contentlet.getContentType()).thenReturn(contentType);
Mockito.when(contentlet.getBinaryMetadata("productImage")).thenReturn(null);

final Map<String, Object> outputMap = new HashMap<>();
strategy.transform(contentlet, outputMap, Set.of(AVOID_MAP_SUFFIX_FOR_VIEWS), null);

assertFalse(
"emptyMap() must NOT be written to the output map for a binary field with no "
+ "uploaded file — it poisons the contentlet backing map and causes "
+ "ClassCastException in Contentlet.getBinary()",
outputMap.containsKey("productImage"));
}

/**
* Verifies that the static {@code transform(Field, Contentlet)} helper returns an empty map
* (the sentinel value) when the contentlet has no metadata for the field. The instance method
* uses the emptiness of this result to decide whether to write to the output map.
*/
@Test
public void testStaticTransform_whenBinaryMetadataIsNull_returnsEmptyMap() throws Exception {
final Field field = Mockito.mock(BinaryField.class);
Mockito.when(field.variable()).thenReturn("productImage");

final Contentlet contentlet = Mockito.mock(Contentlet.class);
Mockito.when(contentlet.getBinaryMetadata("productImage")).thenReturn(null);

final Map<String, Object> result = BinaryViewStrategy.transform(field, contentlet);

assertTrue("Static transform must return emptyMap() when metadata is null",
result.isEmpty());
}
}
Loading