.cursor/skills/owasp-checklist/SKILL.md
L1 child skill under security-reviewer: OWASP Top 10 checklist and Spring/Thymeleaf secure patterns. Auto-load with security-reviewer when Java/Spring files are in scope; used by security-auditor for Spring-deep reviews.
npx skillsauth add BhumitThakkar/cursor-kit owasp-checklistInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
security-reviewer — workspace posture, secrets, Git/AI exposure, dependencies, Docker/CI. Always read the parent skill’s “Knowledge hierarchy” and “Credentials, Git, and AI” sections before applying the A01–A10 tables below, unless Zeus explicitly scoped a trivial one-file check and waived L0 in the brief./cmd-review-project-security or security-review.mdc activation that includes Java/Spring artifacts (see parent “When to auto-load owasp-checklist”).Check for:
@RestController method has explicit access control: @PreAuthorize, @Secured, or a SecurityConfig rule/admin/**, /api/admin/**) are restricted to ADMIN roleSpring pattern:
// Correct — method-level security
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/admin/users")
public List<UserResponse> getAllUsers() { ... }
// Correct — ownership check in service
public BookingResponse getBooking(Long id, String currentUserEmail) {
Booking booking = repository.findById(id).orElseThrow(...);
if (!booking.getOwnerEmail().equals(currentUserEmail)) {
throw new AccessDeniedException("Not your booking");
}
return toResponse(booking);
}
Check for:
server.ssl.* configured or handled at load balancerSpring pattern:
// Correct
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12);
}
Check for:
JdbcTemplate with ? placeholders@Query with user input concatenated into the stringSpring pattern:
// CORRECT — parameterized
@Query("SELECT b FROM Booking b WHERE b.ownerEmail = :email")
List<Booking> findByOwnerEmail(@Param("email") String email);
// WRONG — injection risk
@Query("SELECT b FROM Booking b WHERE b.ownerEmail = '" + email + "'")
Check for:
@PreAuthorize — not in the controller with if-statementsCheck for:
spring.jpa.show-sql=false in production profileserver.error.include-stacktrace=neverallowedOrigins("*") in production/actuator/** not publicDEBUG logging not enabled in productionSpring pattern:
// Correct CORS config
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("https://shreejalarammandir.org"));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
source.registerCorsConfiguration("/api/**", config);
return source;
}
Check for:
pom.xml<!-- pom.xml — dependency check plugin -->
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>9.0.9</version>
<configuration>
<failBuildOnCVSS>7</failBuildOnCVSS>
</configuration>
</plugin>
Check for:
# application.properties
server.servlet.session.timeout=30m
spring.security.oauth2.resourceserver.jwt.issuer-uri=${JWT_ISSUER}
Check for:
ObjectInputStream deserializing untrusted dataCheck for:
log.warn("Login failed for user: {}", email){} placeholder, never concatenatedlog4j2*.xml (or YAML) has no secrets; pattern layouts do not enable risky lookups on user-controlled message data; file appenders not directed to world-writable paths in prod// CORRECT — safe logging
log.info("User login attempt for: {}", email);
// WRONG — log injection risk
log.info("User login attempt for: " + email);
// WRONG — logs sensitive data
log.debug("Processing payment: {}", paymentRequest); // exposes card details
Check for:
RestTemplate or WebClient calls do not use raw user input as the URL// CORRECT — allowlist validation before fetch
private static final List<String> ALLOWED_HOSTS = List.of("api.example.com");
public String fetchFromTrustedSource(String url) {
URI uri = URI.create(url);
if (!ALLOWED_HOSTS.contains(uri.getHost())) {
throw new SecurityException("Host not allowed: " + uri.getHost());
}
return restTemplate.getForObject(url, String.class);
}
@PostMapping("/upload")
public ResponseEntity<?> upload(@RequestParam("file") MultipartFile file) {
// 1. Size check
if (file.getSize() > 5 * 1024 * 1024) {
throw new ValidationException("File too large — max 5MB");
}
// 2. MIME type check — not extension alone
String contentType = file.getContentType();
if (!List.of("image/jpeg", "image/png", "image/webp").contains(contentType)) {
throw new ValidationException("File type not allowed");
}
// 3. Sanitise filename — remove path traversal
String originalName = StringUtils.cleanPath(file.getOriginalFilename());
if (originalName.contains("..")) {
throw new ValidationException("Invalid filename");
}
// 4. Save outside web root — use Path API
Path uploadPath = Paths.get(uploadDir).resolve(originalName).normalize();
Files.copy(file.getInputStream(), uploadPath, StandardCopyOption.REPLACE_EXISTING);
return ResponseEntity.ok().build();
}
password= literals — use git-secrets, TruffleHog, or GitHub secret scanning.SecurityFilterChain matches all dispatcher paths; method security (@PreAuthorize) where needed.development
Read-only checklist for Jakarta Bean Validation on Spring API models (DTOs, request bodies). Produces a markdown report with PASS/FAIL; does not modify source files.
data-ai
Thymeleaf + Spring Boot UI conventions, fragment patterns, form handling, and accessibility standards for this project.
development
QA-oriented playbook for verifying test coverage on new or changed code (JaCoCo / Maven Gradle) against project quality gate expectations. Read-only analysis instructions unless user authorizes runs.
testing
Maintain AGENTS.md roster and disabled.txt without deleting agent files without explicit approval.