CVE-2021-21246 (Java, published 2021-01-15T21:15Z)
Description: value: OneDev is an all-in-one devops platform. In OneDev before version 4.0.3, the REST UserResource endpoint performs a security check to make sure that only administrators can list user details. However for the `/users/{id}` endpoint there are no security checks enforced so it is possible to retrieve arbitrary user details including their Access Tokens! These access tokens can be used to access the API or clone code in the build spec via the HTTP(S) protocol. It has permissions to all projects accessible by the user account. This issue may lead to `Sensitive data leak` and leak the Access Token which can be used to impersonate the administrator or any other users. This issue was addressed in 4.0.3 by removing user info from restful api.
Severity: MEDIUM (CVSS v3 base score: 7.5)
Weakness: CWE-862 \u2014 Missing Authorization
The product does not perform an authorization check when an actor attempts to access a resource or perform an action.
Repository: https://github.com/theonedev/onedev
Files changed:
server-core/src/main/java/io/onedev/server/model/User.java
Commit message: Fix the issue that user access token can be leaked via restful api

Vulnerable code:
package io.onedev.server.rest;

import java.util.Collection;

import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;

import org.apache.shiro.authz.UnauthorizedException;
import org.hibernate.criterion.Restrictions;
import org.hibernate.validator.constraints.Email;

import io.onedev.server.entitymanager.UserManager;
import io.onedev.server.model.User;
import io.onedev.server.persistence.dao.EntityCriteria;
import io.onedev.server.rest.jersey.ValidQueryParams;
import io.onedev.server.security.SecurityUtils;

@Path("/users")
@Consumes(MediaType.WILDCARD)
@Produces(MediaType.APPLICATION_JSON)
@Singleton
public class UserResource {

	private final UserManager userManager;
	
	@Inject
	public UserResource(UserManager userManager) {
		this.userManager = userManager;
	}
	
	@ValidQueryParams
	@GET
	public Response query(@QueryParam("name") String name, @Email @QueryParam("email") String email, 
			@QueryParam("offset") Integer offset, @QueryParam("count") Integer count, @Context UriInfo uriInfo) {
    	if (!SecurityUtils.isAdministrator())
    		throw new UnauthorizedException("Unauthorized access to user profiles");
    	
    	EntityCriteria<User> criteria = EntityCriteria.of(User.class);
    	if (name != null)
    		criteria.add(Restrictions.eq("name", name));
		if (email != null)
			criteria.add(Restrictions.eq("email", email));
		
    	if (offset == null)
    		offset = 0;
    	
    	if (count == null || count > RestConstants.PAGE_SIZE) 
    		count = RestConstants.PAGE_SIZE;

    	Collection<User> users = userManager.query(criteria, offset, count);
		
		return Response.ok(users, RestConstants.JSON_UTF8).build();
	}
	
    @GET
    @Path("/{userId}")
    public User get(@PathParam("userId") Long userId) {
    	return userManager.load(userId);
    }
    
}

Fixed code:
	@JsonView(DefaultView.class)
	@JsonView(DefaultView.class)