Skip to content
Draft
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 @@ -26,7 +26,9 @@
import com.vaadin.flow.component.PropertyDescriptor;
import com.vaadin.flow.component.PropertyDescriptors;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.internal.UrlUtil;
import com.vaadin.flow.server.AbstractStreamResource;
import com.vaadin.flow.server.InitParameters;
import com.vaadin.flow.server.StreamResource;
import com.vaadin.flow.server.StreamResourceRegistry;
import com.vaadin.flow.server.streams.AbstractDownloadHandler;
Expand Down Expand Up @@ -70,6 +72,11 @@ public Anchor() {
* the href to set
* @param text
* the text content to set
* @throws IllegalArgumentException
* if {@code href} uses a scheme that is not considered safe;
* see {@link #setUnsafeHref(String)} and the
* {@value InitParameters#URL_SAFE_SCHEMES} configuration
* property
*/
public Anchor(String href, String text) {
setHref(href);
Expand All @@ -96,6 +103,11 @@ public Anchor(String href, String text) {
* the href to set
* @param textSignal
* the signal to bind, not {@code null}
* @throws IllegalArgumentException
* if {@code href} uses a scheme that is not considered safe;
* see {@link #setUnsafeHref(String)} and the
* {@value InitParameters#URL_SAFE_SCHEMES} configuration
* property
* @since 25.1
*/
public Anchor(String href, Signal<String> textSignal) {
Expand All @@ -117,6 +129,11 @@ public Anchor(String href, Signal<String> textSignal) {
* the text content to set
* @param target
* the target window, tab or frame
* @throws IllegalArgumentException
* if {@code href} uses a scheme that is not considered safe;
* see {@link #setUnsafeHref(String)} and the
* {@value InitParameters#URL_SAFE_SCHEMES} configuration
* property
*/
public Anchor(String href, String text, AnchorTarget target) {
setHref(href);
Expand Down Expand Up @@ -248,6 +265,11 @@ public Anchor(DownloadHandler downloadHandler,
* the href to set
* @param components
* the components to add
* @throws IllegalArgumentException
* if {@code href} uses a scheme that is not considered safe;
* see {@link #setUnsafeHref(String)} and the
* {@value InitParameters#URL_SAFE_SCHEMES} configuration
* property
*/
public Anchor(String href, Component... components) {
setHref(href);
Expand All @@ -268,8 +290,40 @@ public Anchor(String href, Component... components) {
*
* @param href
* the href to set
* @throws IllegalArgumentException
* if the URL uses a scheme that is not considered safe; see
* {@link #setUnsafeHref(String)} and the
* {@value InitParameters#URL_SAFE_SCHEMES} configuration
* property
*/
public void setHref(String href) {
if (href == null) {
throw new IllegalArgumentException("Href must not be null");
}
if (!UrlUtil.isSafeUrl(href)) {
throw new IllegalArgumentException(UrlUtil.getUnsafeUrlMessage(
"href", href, "setUnsafeHref(String)"));
}
this.href = href;
assignHrefAttribute();
}

/**
* Sets the URL that this anchor links to without validating its scheme.
* <p>
* Unlike {@link #setHref(String)}, this method does not reject URLs based
* on the {@value InitParameters#URL_SAFE_SCHEMES} configuration. Use it
* only for URLs that are fully under your control and known to be safe,
* such as a hard-coded {@code javascript:} or {@code data:} URL. Passing
* untrusted input here can expose the application to cross-site scripting
* (XSS) attacks.
*
* @see #setHref(String)
*
* @param href
* the href to set
*/
public void setUnsafeHref(String href) {
if (href == null) {
throw new IllegalArgumentException("Href must not be null");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
import com.vaadin.flow.component.PropertyDescriptor;
import com.vaadin.flow.component.PropertyDescriptors;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.internal.UrlUtil;
import com.vaadin.flow.server.AbstractStreamResource;
import com.vaadin.flow.server.InitParameters;
import com.vaadin.flow.server.StreamResource;
import com.vaadin.flow.server.streams.AbstractDownloadHandler;
import com.vaadin.flow.server.streams.DownloadHandler;
Expand Down Expand Up @@ -131,6 +133,11 @@ public IFrame() {
*
* @param src
* Source URL
* @throws IllegalArgumentException
* if {@code src} uses a scheme that is not considered safe; see
* {@link #setUnsafeSrc(String)} and the
* {@value InitParameters#URL_SAFE_SCHEMES} configuration
* property
*/
public IFrame(String src) {
setSrc(src);
Expand Down Expand Up @@ -160,8 +167,36 @@ public IFrame(DownloadHandler downloadHandler) {
*
* @param src
* Source URL.
* @throws IllegalArgumentException
* if the URL uses a scheme that is not considered safe; see
* {@link #setUnsafeSrc(String)} and the
* {@value InitParameters#URL_SAFE_SCHEMES} configuration
* property
*/
public void setSrc(String src) {
if (src != null && !UrlUtil.isSafeUrl(src)) {
throw new IllegalArgumentException(UrlUtil
.getUnsafeUrlMessage("src", src, "setUnsafeSrc(String)"));
}
set(srcDescriptor, src);
}

/**
* Sets the source of the iframe without validating its scheme.
* <p>
* Unlike {@link #setSrc(String)}, this method does not reject URLs based on
* the {@value InitParameters#URL_SAFE_SCHEMES} configuration. Use it only
* for URLs that are fully under your control and known to be safe, such as
* a hard-coded {@code javascript:} or {@code data:} URL. Passing untrusted
* input here can expose the application to cross-site scripting (XSS)
* attacks.
*
* @see #setSrc(String)
*
* @param src
* Source URL.
*/
public void setUnsafeSrc(String src) {
set(srcDescriptor, src);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.vaadin.flow.component.PropertyDescriptors;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.server.AbstractStreamResource;
import com.vaadin.flow.server.InitParameters;
import com.vaadin.flow.server.StreamResource;
import com.vaadin.flow.server.streams.AbstractDownloadHandler;
import com.vaadin.flow.server.streams.DownloadHandler;
Expand Down Expand Up @@ -196,6 +197,10 @@ public String getSrc() {

/**
* Sets the image URL.
* <p>
* Unlike {@link Anchor#setHref(String)} and {@link IFrame#setSrc(String)},
* image URLs are not validated against the
* {@value InitParameters#URL_SAFE_SCHEMES} configuration.
*
* @param src
* the image URL
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@
import com.vaadin.flow.server.AbstractStreamResource;
import com.vaadin.flow.server.streams.DownloadHandler;
import com.vaadin.flow.server.streams.ServletResourceDownloadHandler;
import com.vaadin.flow.signals.local.ValueSignal;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

class AnchorTest extends ComponentTest {
Expand Down Expand Up @@ -428,6 +430,47 @@
"Custom download handlers should by default add download attribute");
}

@Test
void setHref_unsafeScheme_throws() {
Anchor anchor = new Anchor();
assertThrows(IllegalArgumentException.class,
() -> anchor.setHref("javascript:alert(1)"));
}

@Test
void setUnsafeHref_unsafeScheme_setsHrefWithoutValidation() {
Anchor anchor = new Anchor();
anchor.setUnsafeHref("javascript:alert(1)");
assertEquals("javascript:alert(1)",
anchor.getElement().getAttribute("href"));
}

@Test
void constructor_stringHrefStringText_unsafeScheme_throws() {
assertThrows(IllegalArgumentException.class,
() -> new Anchor("javascript:alert(1)", "Click"));
}

@Test
void constructor_stringHrefSignalText_unsafeScheme_throws() {
assertThrows(IllegalArgumentException.class,

Check warning on line 456 in flow-html-components/src/test/java/com/vaadin/flow/component/html/AnchorTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor the code of the lambda to have only one invocation possibly throwing a runtime exception.

See more on https://sonarcloud.io/project/issues?id=vaadin_flow&issues=AZ6xtmb_lrIXgwXJSwCK&open=AZ6xtmb_lrIXgwXJSwCK&pullRequest=24539
() -> new Anchor("javascript:alert(1)",
new ValueSignal<>("Click")));
}

@Test
void constructor_stringHrefStringTextTarget_unsafeScheme_throws() {
assertThrows(IllegalArgumentException.class,
() -> new Anchor("javascript:alert(1)", "Click",
AnchorTarget.BLANK));
}

@Test
void constructor_stringHrefComponents_unsafeScheme_throws() {
assertThrows(IllegalArgumentException.class,
() -> new Anchor("javascript:alert(1)"));
}

private void mockUI() {
ui = new UI();
UI.setCurrent(ui);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,11 @@ private static boolean isSpecialSetter(Method method) {
return true;
}

if (method.getDeclaringClass() == IFrame.class
&& method.getName().equals("setUnsafeSrc")) {
return true;
}

if (method.getDeclaringClass() == HtmlObject.class
&& method.getName().startsWith("setData")
&& method.getParameterTypes()[0] == DownloadHandler.class) {
Expand All @@ -275,6 +280,11 @@ private static boolean isSpecialSetter(Method method) {
return true;
}

if (method.getDeclaringClass() == Anchor.class
&& method.getName().equals("setUnsafeHref")) {
return true;
}

if (method.getDeclaringClass() == Image.class
&& method.getName().startsWith("setSrc")
&& method.getParameterTypes()[0] == DownloadHandler.class) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

class IFrameTest extends ComponentTest {
Expand Down Expand Up @@ -116,4 +117,24 @@ public Element getElement() {
new TestIFrame(handler);
assertTrue(handler.isInline());
}

@Test
void setSrc_unsafeScheme_throws() {
IFrame iframe = new IFrame();
assertThrows(IllegalArgumentException.class,
() -> iframe.setSrc("javascript:alert(1)"));
}

@Test
void setUnsafeSrc_unsafeScheme_setsSrcWithoutValidation() {
IFrame iframe = new IFrame();
iframe.setUnsafeSrc("javascript:alert(1)");
assertEquals("javascript:alert(1)", iframe.getSrc());
}

@Test
void constructor_unsafeSrc_throws() {
assertThrows(IllegalArgumentException.class,
() -> new IFrame("javascript:alert(1)"));
}
}
Loading
Loading