HTML escape strings - java

Spring has htmlEscape to escape potentially unsafe HTML from user input. I'd like to apply htmlEscape as an annotation to the backend Spring based code (similar concept). This is to help prevent XSS attacks.
class MyCreateRequestDoc {
#NotBlank(message = 'A name must be specified')
#AsSafeHtml
String name
I believe this calls for an AOP pattern, but I don't know how to implement such a thing in Java Spring.
I started with:
package com.my.company.services..security
import groovy.util.logging.Slf4j
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.springframework.stereotype.Component
import org.springframework.web.util.HtmlUtils
#Aspect
#Slf4j
#Component
class AsSafeHtmlAspect {
#Around("#within(com.my.company.services.security.AsSafeHtml)")
void assertProperty(ProceedingJoinPoint pjp){
var input = 'get this from somewhere'
return toSafeHtml(input) //return?
}
private String toSafeHtml(input) {
return HtmlUtils.htmlEscape(input)
}
}
And the related annotation used in Around above:
package com.my.company.services.security
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
#Retention(RetentionPolicy.RUNTIME)
#interface AsSafeHtml {
}

Related

Unable to get annotations from Java classes when trying to autowire multiple implementations

In a Java 11/Spring REST API project I have an interface with multiple implementations. I want to choose the implementation in the configuration (in this case, application.yml file):
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
#Retention(RUNTIME)
#Target(TYPE)
public #interface PickableImplementation {
// This id will match an entry in the config file
public String id() default "";
}
So I have the two possible "pickable" implementations:
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import com.mycompany.api.util.PickableImplementation;
#Component
#Service
#PickableImplementation(id = "s3")
public class BatchProcessServiceS3 implements BatchProcessService {
// Some implementation biz logic
}
// In another file, with the same imports:
#Component
#Service
#PickableImplementation(id = "azure")
public class BatchProcessServiceAzure implements BatchProcessService {
// Another implementation biz logic
}
In the consumer class which in this case is a Controller, I try to pick the desired implementation:
import com.mycompany.api.util.PickableImplementation;
import java.util.List;
#RestController
#RequestMapping("/batch")
public class BatchController {
#Autowired private Environment env;
private BatchProcessService batchProcessService;
private final List<BatchProcessService> batchProcessImplementations;
public BatchController(List<BatchProcessService> batchProcessImplementations,
Environment environment){
this.env = environment;
var activeSvcImplementationId = env.getRequiredProperty("buckets-config.active");
this.batchProcessImplementations = batchProcessImplementations;
for (var batchProcessService : this.batchProcessImplementations) {
if(batchProcessService.getClass().isAnnotationPresent(PickableImplementation.class)) {
// Verify the id, compare with the one from config file, etc.
}
}
}
}
Expected behavior: inside the loop, I expected to get the annotations of each implementation, traverse the list, verify if it matches with the one with the application.yml and if it does, pick it to populate the service layer (private BatchProcessService batchProcessService).
Actual behavior: not only the isAnnotationPresent() method returns false, but also if I try getAnnotations() I get an empty array, like there are no annotations in the class. And besides my custom one, there are at least two additional annotations (Component, Service and others related to logging and the like).
As another puzzling detail, if I run getAnnotations() on the qualified name of the class in the middle of a debugging session, the annotations are present. But in that very moment, running that method on the elements on the list return 0 annotations.
I've run out of ideas, has anyone tried this same combination of autowiring several implementations and at the same time, relying in custom annotations?
Additional references:
Custom Annotations in Java:
https://www.baeldung.com/java-custom-annotation
Autowired:
https://www.baeldung.com/spring-autowire
Useful answer on autowiring multiple implementations:
https://stackoverflow.com/a/51778396/6315428

Custom AOP spring boot annotation with ribbon client blocking api call with return "1"

I have really poor experience with ribbon/eureka so forgive me if this is a stupid question:
I have two different microservice both connected to a discovery server, the first one calls the second using a custom annotation that sends a request using rest template.
Custom annotation name is PreHasAuthority
Controller :
#PreHasAuthority(value="[0].getProject()+'.requirements.update'")
#PostMapping(CREATE_UPDATE_REQUIREMENT)
public ResponseEntity<?> createUpdateRequirement(#Valid #RequestBody RequirementDTO requirementDTO
, HttpServletRequest request, HttpServletResponse response) {
return requirementService.createUpdateRequirement(requirementDTO, request, response);
}
Annotation interface :
import java.lang.annotation.*;
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface PreHasAuthority {
String value();
}
Annotation implementation:
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;
import netcomgroup.eu.service.AuthenticationService;
#Aspect
#Component
public class PreHasAuthorityServiceAspect {
#Autowired
private AuthenticationService authenticationService;
#Around(value = "#annotation(PreHasAuthority)")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
PreHasAuthority preHasAuthority = method.getAnnotation(PreHasAuthority.class);
Object[] args = joinPoint.getArgs();
String permission = preHasAuthority.value();
ExpressionParser elParser = new SpelExpressionParser();
Expression expression = elParser.parseExpression(permission);
String per = (String) expression.getValue(args);
String token =null;
for(Object o : args) {
if(o instanceof HttpServletRequest) {
HttpServletRequest request = (HttpServletRequest)o;
token=request.getHeader("X-Auth");
break;
}
}
if(token==null) {
throw new IllegalArgumentException("Token not found");
}
boolean hasPerm = authenticationService.checkPermission(per,token);
if(!hasPerm)
throw new Exception("Not Authorized");
}
}
My Ribbon configuration
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RoundRobinRule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
public class RibbonConfiguration {
#Autowired
IClientConfig config;
#Bean
public IRule ribbonRule(IClientConfig config) {
return new RoundRobinRule();
}
}
Eureka config in application properties
#Eureka config
eureka.client.serviceUrl.defaultZone= http://${registry.host:localhost}:${registry.port:8761}/eureka/
eureka.client.healthcheck.enabled= true
eureka.instance.leaseRenewalIntervalInSeconds= 10
eureka.instance.leaseExpirationDurationInSeconds= 10
by calling the api from postman request is sendend correctly to the second microservice and i'm certain the return is "true".
After that the request stops before entering the createUpdateRequirement method and returns '1' as postman body response. No error of sort is provided.
My guess is that the problem resides within the custom annotation, cause when i remove the annotation the api call works perfectly, but i cannot understand the problem as it seems all setted up correctly to me.
Your #Around advice never calls joinPoint.proceed(). Hence, the intercepted target method will never be executed.
The second problem is that your advice method returns void, i.e. it will never match any method returning another type such as the ResponseEntity<?> createUpdateRequirement(..) method.
Besides, around is a reserved keyword in native AspectJ syntax. Even though it might work in annotation-driven syntax, you ought to rename your advice method to something else like aroundAdvice or interceptPreHasAuthority - whatever.
Please do read an AspectJ or Spring AOP tutorial, especially the Spring manual's AOP chapter. 😉

Add a header to RestTemplate using #Before advice

I'm having trouble adding a custom Header to a RestTemplate using AOP in Spring.
What I have in mind is having some advice that will automatically modify execution of RestTemplate.execute(..) by adding this one Header. Other concern is targeting a particular RestTemplate instance that is a member of a Service which requires having this Header passed.
Here is the code of my Advice as it looks like now:
package com.my.app.web.controller.helper;
import com.my.app.web.HeaderContext;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.RestTemplate;
#Aspect
public class MyAppHeaderAspect {
private static final String HEADER_NAME = "X-Header-Name";
#Autowired
HeaderContext headerContext;
#Before("withinServiceApiPointcut() && executionOfRestTemplateExecuteMethodPointcut()")
public void addHeader(RequestCallback requestCallback){
if(requestCallback != null){
String header = headerContext.getHeader();
}
}
#Pointcut("within (com.my.app.service.NeedsHeaderService)")
public void withinServiceApiPointcut() {}
#Pointcut("execution (* org.springframework.web.client.RestTemplate.execute(..)) && args(requestCallback)")
public void executionOfRestTemplateExecuteMethodPointcut(RequestCallback requestCallback) {}
}
The problem that I'm having is how to modify RequestCallback to add my header. Being an interface it looks rather empty, on the other hand I'd rather not use a concrete implemntation because then I'd have to manually check if the implementation matches expected class. I'm beginning to wonder if this is really a correct method to do this. I found this answer Add my custom http header to Spring RestTemplate request / extend RestTemplate
But it uses RestTemplate.exchange() when I've checked that on my execution path RestTemplate.execute() is being used. Anyone has any ideas here?

How would I write an automated check that every parameter has a specific annotation?

I'm writing a Rest API and my automated tests are calling the class directly without deploying the to the server. As an example, I am testing this method:
#GET
#Path("/{referenceId}")
#Produces("application/json")
public String findByReferenceId(#PathParam("referenceId") String referenceId,
String view) {
My tests are checking that the logic works and they pass. But this code has a bug: I forgot to put a #QueryParam annotation on that view parameter. So this code works when tested, but if you try to use this resource on the deployed app, the view parameter will never be settable.
There are many ways I can solve this, but my current preference is to somehow write an automated check that if a method has a #Path annotation, then every parameter must have either a #PathParam, a #QueryParam or whatever other valid annotation can be there.
I prefer this over a new end-to-end test, because my other tests are already covering 95% of that logic. I just don't know how to automate this check. I'm using Maven and CXF (which means I'm using Spring). I'm hoping there's a plugin that can be configured to do this.
Something I just realized: It's valid to have a single parameter without an annotation. When you do this, jax-rs sets it to the entity you pass in. I'm not sure how to deal with this scenario. I could create my own custom annotation called #Payload and tell people to use it, but something seems wrong about that.
Here's my solution. In the end, I decided to create a #RawPayload annotation. Otherwise, I can't know if the missing annotation is intentional or not. Here's where I got the Reflections class: https://code.google.com/p/reflections/
import org.junit.Test;
import org.reflections.Reflections;
import org.reflections.scanners.MethodAnnotationsScanner;
import javax.ws.rs.Path;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Set;
import static org.junit.Assert.assertTrue;
...
#Test
public void testAllParametersAreAnnotated() throws Exception {
String message = "You are missing a jax-rs annotation on a method's parameter: ";
Reflections reflections = new Reflections("package.for.my.services", new MethodAnnotationsScanner());
Set<Method> resourceMethods = reflections.getMethodsAnnotatedWith(Path.class);
assertTrue(resourceMethods.size() > 0);
for (Method resourceMethod : resourceMethods) {
for (int i = 0; i < resourceMethod.getGenericParameterTypes().length; i++) {
Annotation[] annotations = resourceMethod.getParameterAnnotations()[i];
boolean annotationExists = annotations.length > 0;
assertTrue(message +
resourceMethod.getDeclaringClass().getCanonicalName() +
"#" +
resourceMethod.getName(),
annotationExists && containsJaxRsAnnotation(annotations));
}
}
}
private boolean containsJaxRsAnnotation(Annotation[] annotations) {
for (Annotation annotation : annotations) {
if (annotation instanceof RawPayload) {
return true;
}
if (annotation.annotationType().getCanonicalName().startsWith("javax.ws.rs")) {
return true;
}
}
return false;
}
Here's my annotation:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* I'm creating this marker so that we can put it on raw payload params. This is normally unnecessary,
* but it lets me write a very useful automated test.
*/
#Retention(RetentionPolicy.RUNTIME)
public #interface RawPayload {
}

How to implement #RequestMapping custom properties

As an example, take subdomain mapping.
This article: Managing multiple Domain and Sub Domain on Google App Engine for Same Application
recommends to resolve subdomain on Filter and assign variable to ServletRequest headers.
Then the mapping will look like this:
#RequestMapping(value = "/path", headers="subdomain=www")
public String subsiteIndexPage(Model model,HttpServletRequest request) { ... }
If we'd like to create custom #RequestMapping property, such as subdomain, eg. to create mapping like this:
#RequestMapping(value = "/some/action", subdomain = "www")
public String handlerFunction(){ ... }
we should override #RequestMapping #interface definition and override RequestMappingHandlerMapping protected methods, with our own implementation
(as stated on JIRA: "Allow custom request mapping conditions SPR-7812").
Is it right? Can anybody provide a hint, how to achieve this functionality?
Idea 1:
As suggested on original jira thread, is to create own implementation of RequestCondition
There is an project which uses this solution available on github: https://github.com/rstoyanchev/spring-mvc-31-demo/
And related SO question: Adding custom RequestCondition's in Spring mvc 3.1
Maybe mapping like #Subdomain("www") for both Type and Method, is possible solution?
Link to same question on forum.springsource.com
I've created solution based on referenced spring-mvc-31-demo
This solution can be used to map only single RequestCondition as of now. I've created two Issues to notify, this should be changed:
https://github.com/rstoyanchev/spring-mvc-31-demo/issues/5
https://jira.springsource.org/browse/SPR-9350
This solution uses custom #RequestCondition feature of Spring 3.1.1.RELEASE platform
USAGE
Example 1:
#Controller
#SubdomainMapping(value = "subdomain", tld = ".mydomain.com")
class MyController1 {
// Code here will be executed only on address match:
// subdomain.mydomain.com
}
Example 2:
#Controller
class MyController2 {
#RequestMapping("/index.html")
#SubdomainMapping("www")
public function index_www(Map<Object, String> map){
// on www.domain.com
// where ".domain.com" is defined in SubdomainMapping.java
}
#RequestMapping("/index.html")
#SubdomainMapping("custom")
public function index_custom(Map<Object, String> map){
// on custom.domain.com
// where ".domain.com" is defined in SubdomainMapping.java
}
}
We need three files
SubdomainMapping.java
SubdomainRequestCondition.java
SubdomainRequestMappingHandlerMapping.java
SubdomainMapping.java
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface SubdomainMapping {
/**
* This param defines single or multiple subdomain
* Where the Method/Type is valid to be called
*/
String[] value() default {};
/**
* This param defines site domain and tld
* It's important to put the leading dot
* Not an array, so cannot be used for mapping multiple domains/tld
*/
String tld() default ".custom.tld";
}
SubdomainRequestCondition.java
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
public class SubdomainRequestCondition implements
RequestCondition<SubdomainRequestCondition> {
private final Set<String> subdomains;
private final String tld;
public SubdomainRequestCondition(String tld, String... subdomains) {
this(tld, Arrays.asList(subdomains));
}
public SubdomainRequestCondition(String tld, Collection<String> subdomains) {
this.subdomains = Collections.unmodifiableSet(new HashSet<String>(
subdomains));
this.tld = tld;
}
#Override
public SubdomainRequestCondition combine(SubdomainRequestCondition other) {
Set<String> allRoles = new LinkedHashSet<String>(this.subdomains);
allRoles.addAll(other.subdomains);
return new SubdomainRequestCondition(tld, allRoles);
}
#Override
public SubdomainRequestCondition getMatchingCondition(
HttpServletRequest request) {
try {
URL uri = new URL(request.getRequestURL().toString());
String[] parts = uri.getHost().split(this.tld);
if (parts.length == 1) {
for (String s : this.subdomains) {
if (s.equalsIgnoreCase(parts[0])) {
return this;
}
}
}
} catch (Exception e) {
e.printStackTrace(System.err);
}
return null;
}
#Override
public int compareTo(SubdomainRequestCondition other,
HttpServletRequest request) {
return org.apache.commons.collections.CollectionUtils.removeAll(other.subdomains, this.subdomains).size();
}
}
SubdomainRequestMappingHandlerMapping.java
import java.lang.reflect.Method;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
public class CustomRequestMappingHandlerMapping extends
RequestMappingHandlerMapping {
#Override
protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
SubdomainMapping typeAnnotation = AnnotationUtils.findAnnotation(
handlerType, SubdomainMapping.class);
return createCondition(typeAnnotation);
}
#Override
protected RequestCondition<?> getCustomMethodCondition(Method method) {
SubdomainMapping methodAnnotation = AnnotationUtils.findAnnotation(
method, SubdomainMapping.class);
return createCondition(methodAnnotation);
}
private RequestCondition<?> createCondition(SubdomainMapping accessMapping) {
return (accessMapping != null) ? new SubdomainRequestCondition(
accessMapping.tld(), accessMapping.value()) : null;
}
}
Instalation
IMPORTANT: So far, it is not possible to use this solution with XML element
<mvc:annotation-driven />, see JIRA https://jira.springsource.org/browse/SPR-9344 for explanation
You have to register custom MappingHandler bean, pointing at this custom implementation SubdomainRequestMappingHandlerMapping class
You have to set it's order to be lower than default RequestMappingHandlerMapping
OR
Replace the registered RequestMappingHandlerMapping (possibly on order=0)
For more wide explanation on implementing this solution, see the related github project
That's correct, but that would be too complicated. You'd better check the Host header, whether it contains a given subdomain.
But you should not really need this more than once or twice, so you can also do it manually in the method body. If you really need it in many places, it would be an odd requirement.

Categories

Resources