-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Description
Improper Access Control in InstanceController
Report Information
Project: PowerJob
Version: 5.1.2
Reporter: Zhang Yichao (Student, Beijing University of Posts and Telecommunications)
Discovery Date: November 21, 2025
Report Date: November 21, 2025
Severity: Critical
Executive Summary
This report identifies critical security vulnerabilities in the PowerJob server's InstanceController class, specifically related to improper access control mechanisms. The vulnerabilities allow unauthorized users to access sensitive instance data, download log files, and perform operations on instances belonging to other applications. These issues pose significant security risks including data leakage, unauthorized access to sensitive information, and potential privilege escalation.
Vulnerability Details
1. Critical: Unauthenticated Log File Download (CVE-2024-XXXX)
Location: /instance/downloadLog endpoint
Severity: Critical
CVSS Score: 8.5
@GetMapping("/downloadLog")
public void downloadLogFile(Long instanceId , HttpServletResponse response) throws Exception {
File file = instanceLogService.downloadInstanceLog(instanceId);
OmsFileUtils.file2HttpResponse(file, response);
}Issue: This endpoint lacks any authentication or authorization checks. Any user with knowledge of an instanceId can download log files from any application instance.
Impact:
- Unauthorized access to sensitive log data
- Information disclosure across application boundaries
- Potential exposure of credentials, API keys, or business logic
2. High: Insufficient Authorization in Instance Detail Access
Location: /instance/detailPlus endpoint
Severity: High
CVSS Score: 7.2
@PostMapping("/detailPlus")
public ResultDTO<InstanceDetailVO> getInstanceDetailPlus(@RequestBody QueryInstanceDetailRequest req, HttpServletRequest hsr) {
req.setAppId(AuthHeaderUtils.fetchAppIdL(hsr));
// Compatibility code for legacy frontend without appId
if (req.getAppId() == null) {
req.setAppId(instanceService.getInstanceInfo(req.getInstanceId()).getAppId());
}
return ResultDTO.success(InstanceDetailVO.from(instanceService.getInstanceDetail(req.getAppId(), req.getInstanceId(), customQuery)));
}Issues:
- Missing
@ApiPermissionannotation - Dangerous compatibility code that bypasses authorization when
appIdis null - No validation that the user has permission to access the specified instance
3. Medium: Inadequate Instance Ownership Validation
Location: Service layer methods
Severity: Medium
CVSS Score: 6.1
The core issue lies in the fetchInstanceInfo method which retrieves instance data without validating ownership:
private InstanceInfoDO fetchInstanceInfo(Long instanceId) {
InstanceInfoDO instanceInfoDO = instanceInfoRepository.findByInstanceId(instanceId);
if (instanceInfoDO == null) {
throw new IllegalArgumentException("invalid instanceId: " + instanceId);
}
return instanceInfoDO; // Returns data without appId validation
}CodeQL Analysis
To systematically identify these vulnerabilities, the following CodeQL query can be used:
/**
* @name Improper Access Control in PowerJob InstanceController
* @description Finds endpoints that lack proper authorization checks for instance access
* @kind problem
* @problem.severity error
* @security-severity 8.5
* @precision high
* @id java/powerjob-instance-access-control
* @tags security
* external/cwe/cwe-862
* external/cwe/cwe-863
*/
import java
import semmle.code.java.frameworks.spring.SpringController
import semmle.code.java.security.RequestForgery
class InstanceControllerMethod extends Method {
InstanceControllerMethod() {
this.getDeclaringType().hasQualifiedName("tech.powerjob.server.web.controller", "InstanceController")
}
}
class ApiPermissionAnnotation extends Annotation {
ApiPermissionAnnotation() {
this.getType().hasQualifiedName("tech.powerjob.server.auth.interceptor", "ApiPermission")
}
}
class SpringMappingAnnotation extends Annotation {
SpringMappingAnnotation() {
this.getType().hasQualifiedName("org.springframework.web.bind.annotation", ["GetMapping", "PostMapping", "RequestMapping"])
}
}
class InstanceIdParameter extends Parameter {
InstanceIdParameter() {
this.getType().hasQualifiedName("java.lang", "Long") and
this.getName().toLowerCase().matches("%instanceid%")
}
}
predicate lacksApiPermission(InstanceControllerMethod method) {
method.hasAnnotation() and
method.getAnAnnotation() instanceof SpringMappingAnnotation and
not method.getAnAnnotation() instanceof ApiPermissionAnnotation
}
predicate hasInstanceIdParameter(InstanceControllerMethod method) {
method.getAParameter() instanceof InstanceIdParameter
}
predicate isPublicEndpoint(InstanceControllerMethod method) {
method.isPublic() and
method.hasAnnotation() and
method.getAnAnnotation() instanceof SpringMappingAnnotation
}
from InstanceControllerMethod method
where lacksApiPermission(method) and
hasInstanceIdParameter(method) and
isPublicEndpoint(method)
select method, "Instance controller method $@ lacks proper authorization checks for instance access",
method, method.getName()Additional CodeQL Query for Service Layer Validation
/**
* @name Missing Instance Ownership Validation
* @description Finds methods that access instance data without validating application ownership
* @kind problem
* @problem.severity warning
* @security-severity 6.1
* @precision medium
* @id java/powerjob-missing-ownership-validation
*/
import java
class InstanceServiceMethod extends Method {
InstanceServiceMethod() {
this.getDeclaringType().hasQualifiedName("tech.powerjob.server.core.instance", "InstanceService")
}
}
class InstanceRepositoryCall extends MethodAccess {
InstanceRepositoryCall() {
this.getMethod().hasName("findByInstanceId") and
this.getMethod().getDeclaringType().hasQualifiedName("tech.powerjob.server.persistence.remote.repository", "InstanceInfoRepository")
}
}
predicate lacksAppIdValidation(InstanceServiceMethod method, InstanceRepositoryCall repoCall) {
repoCall.getEnclosingCallable() = method and
not exists(MethodAccess appIdCheck |
appIdCheck.getEnclosingCallable() = method and
appIdCheck.getMethod().getName().matches("%appId%")
)
}
from InstanceServiceMethod method, InstanceRepositoryCall repoCall
where lacksAppIdValidation(method, repoCall)
select method, "Method $@ accesses instance data without validating application ownership",
method, method.getName()Proof of Concept
Unauthorized Log Download
# Attacker can download logs from any instance without authentication
curl -X GET "http://target-server:7700/instance/downloadLog?instanceId=123456" \
-o stolen_logs.logCross-Application Instance Access
# Attacker can access instance details from other applications
curl -X POST "http://target-server:7700/instance/detailPlus" \
-H "Content-Type: application/json" \
-H "AppId: 999" \
-d '{"instanceId": 123456, "appId": null}'Recommended Remediation
1. Immediate Fixes
Add Authorization to downloadLog endpoint:
@GetMapping("/downloadLog")
@ApiPermission(name = "Instance-DownloadLog", roleScope = RoleScope.APP, requiredPermission = Permission.READ)
public void downloadLogFile(Long instanceId, HttpServletRequest hsr, HttpServletResponse response) throws Exception {
Long appId = AuthHeaderUtils.fetchAppIdL(hsr);
validateInstanceOwnership(appId, instanceId);
File file = instanceLogService.downloadInstanceLog(instanceId);
OmsFileUtils.file2HttpResponse(file, response);
}Fix detailPlus endpoint:
@PostMapping("/detailPlus")
@ApiPermission(name = "Instance-DetailPlus", roleScope = RoleScope.APP, requiredPermission = Permission.READ)
public ResultDTO<InstanceDetailVO> getInstanceDetailPlus(@RequestBody QueryInstanceDetailRequest req, HttpServletRequest hsr) {
req.setAppId(AuthHeaderUtils.fetchAppIdL(hsr));
validateInstanceOwnership(req.getAppId(), req.getInstanceId());
// Remove dangerous compatibility code
return ResultDTO.success(InstanceDetailVO.from(instanceService.getInstanceDetail(req.getAppId(), req.getInstanceId(), req.getCustomQuery())));
}2. Add Instance Ownership Validation
private void validateInstanceOwnership(Long appId, Long instanceId) {
InstanceInfoDO instance = instanceInfoRepository.findByInstanceId(instanceId);
if (instance == null) {
throw new IllegalArgumentException("Instance not found: " + instanceId);
}
if (!instance.getAppId().equals(appId)) {
throw new SecurityException("Access denied: Instance belongs to different application");
}
}3. Service Layer Improvements
Modify the fetchInstanceInfo method to include application validation:
private InstanceInfoDO fetchInstanceInfo(Long instanceId, Long appId) {
InstanceInfoDO instanceInfoDO = instanceInfoRepository.findByInstanceIdAndAppId(instanceId, appId);
if (instanceInfoDO == null) {
throw new IllegalArgumentException("Invalid instanceId or insufficient permissions: " + instanceId);
}
return instanceInfoDO;
}Risk Assessment
Business Impact: High
- Potential data breach affecting multiple applications
- Compliance violations (GDPR, SOX, etc.)
- Loss of customer trust
- Possible regulatory fines
Technical Impact: Critical
- Complete bypass of application-level security
- Unauthorized access to sensitive operational data
- Potential for lateral movement within the system
Conclusion
The identified vulnerabilities in the InstanceController represent serious security flaws that could lead to unauthorized data access and potential system compromise. Immediate action is required to implement proper authorization controls and instance ownership validation. The provided CodeQL queries can be integrated into the development workflow to prevent similar issues in the future.