Coverity is a powerful static application security testing (SAST) tool that identifies critical quality and security defects in Java code before they reach production.
Understanding Coverity
What is Coverity?
- Enterprise-grade static analysis tool
- Identifies security vulnerabilities, code defects, and quality issues
- Uses sophisticated inter-procedural analysis
- Integrates with CI/CD pipelines
Key Capabilities:
- Security Analysis: SQL injection, XSS, path traversal, etc.
- Quality Checks: Null pointer dereferences, resource leaks, concurrency issues
- Architecture Analysis: Software composition analysis
- Compliance: PCI DSS, OWASP, CWE, MISRA compliance reporting
Setup and Installation
1. Coverity Installation
# Download Coverity from Synopsys # Extract and install tar -xzf cov-analysis-linux64-2023.12.0.tar.gz cd cov-analysis-linux64-2023.12.0 ./install.sh # Add to PATH export PATH=/path/to/cov-analysis-linux64-2023.12.0/bin:$PATH
2. Maven Integration
<!-- Coverity Maven Plugin --> <plugin> <groupId>com.synopsys.integration</groupId> <artifactId>coverity-maven-plugin</artifactId> <version>1.1.0</version> <configuration> <coverityUrl>https://coverity.yourcompany.com</coverityUrl> <projectName>your-project</projectName> <streamName>your-stream</streamName> <covBuild>true</covBuild> <covAnalyze>true</covAnalyze> <covCommit>true</covCommit> <failOnError>true</failOnError> </configuration> <executions> <execution> <phase>verify</phase> <goals> <goal>coverity</goal> </goals> </execution> </executions> </plugin>
3. Gradle Integration
plugins {
id 'com.synopsys.integration.coverity' version '1.1.0'
}
coverity {
coverityUrl = 'https://coverity.yourcompany.com'
projectName = 'your-project'
streamName = 'your-stream'
covBuild = true
covAnalyze = true
covCommit = true
failOnError = true
}
Coverity Configuration
1. Coverity Configuration File
{
"coverity": {
"version": "2023.12",
"analysis": {
"java": {
"jvm_args": "-Xmx4G -Xms2G",
"classpath": [
"target/classes",
"target/dependency/*"
],
"source_encoding": "UTF-8"
}
},
"build": {
"command": "mvn compile -q",
"project_dir": "."
},
"commit": {
"host": "coverity.yourcompany.com",
"port": 8443,
"ssl": true,
"user": "coverity-user",
"password": "encrypted-password",
"stream": "your-project-main"
}
}
}
2. Coverity Connect Configuration
# coverity-configure.xml <coverity-configuration> <analysis> <java> <jvm-args>-Xmx4G -Xms2G</jvm-args> <classpath> <dir>target/classes</dir> <dir>target/dependency</dir> </classpath> </java> </analysis> <build> <command>mvn compile -q</command> </build> <triage> <checker-status> <status name="NULL_RETURNS" status="off"/> <status name="RESOURCE_LEAK" status="high"/> <status name="SQL_INJECTION" status="high"/> </checker-status> </triage> </coverity-configuration>
Common Coverity Issues and Solutions
1. Null Pointer Dereference
// COVERITY ISSUE: NULL_RETURNS
public class NullPointerExample {
// BAD: Potential null pointer dereference
public int calculateLength(String input) {
return input.length(); // Coverity: NULL_RETURNS
}
// GOOD: Null check
public int calculateLengthFixed(String input) {
if (input == null) {
return 0;
}
return input.length();
}
// BAD: Method may return null
public String findUserEmail(int userId) {
User user = userRepository.findById(userId);
return user.getEmail(); // Coverity: NULL_RETURNS
}
// GOOD: Handle null case
public String findUserEmailFixed(int userId) {
User user = userRepository.findById(userId);
return user != null ? user.getEmail() : "";
}
// BAD: Chained method calls without null checks
public String getDepartmentName(User user) {
return user.getDepartment().getName(); // Coverity: NULL_RETURNS
}
// GOOD: Safe navigation
public String getDepartmentNameFixed(User user) {
if (user == null || user.getDepartment() == null) {
return "Unknown";
}
return user.getDepartment().getName();
}
}
2. Resource Leak
// COVERITY ISSUE: RESOURCE_LEAK
public class ResourceLeakExample {
// BAD: Resource not closed
public void readFile(String filename) throws IOException {
FileInputStream fis = new FileInputStream(filename); // Coverity: RESOURCE_LEAK
// ... use stream but never close
}
// GOOD: Use try-with-resources
public void readFileFixed(String filename) throws IOException {
try (FileInputStream fis = new FileInputStream(filename)) {
// use stream - automatically closed
}
}
// BAD: Database connection not closed
public List<User> getUsers() throws SQLException {
Connection conn = dataSource.getConnection(); // Coverity: RESOURCE_LEAK
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users");
// ... resources not closed
return parseUsers(rs);
}
// GOOD: Proper resource management
public List<User> getUsersFixed() throws SQLException {
try (Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users")) {
return parseUsers(rs);
}
}
// BAD: Multiple resources in complex logic
public void processFiles(List<String> filenames) throws IOException {
for (String filename : filenames) {
FileInputStream fis = new FileInputStream(filename); // Coverity: RESOURCE_LEAK
processStream(fis);
// fis not closed in loop
}
}
// GOOD: Close resources in loop
public void processFilesFixed(List<String> filenames) throws IOException {
for (String filename : filenames) {
try (FileInputStream fis = new FileInputStream(filename)) {
processStream(fis);
}
}
}
}
3. SQL Injection
// COVERITY ISSUE: SQL_INJECTION
public class SqlInjectionExample {
// BAD: Concatenated SQL query
public User findUserByName(String name) throws SQLException {
String sql = "SELECT * FROM users WHERE name = '" + name + "'"; // Coverity: SQL_INJECTION
try (Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
return mapUser(rs);
}
}
// GOOD: Use PreparedStatement
public User findUserByNameFixed(String name) throws SQLException {
String sql = "SELECT * FROM users WHERE name = ?";
try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, name);
try (ResultSet rs = stmt.executeQuery()) {
return mapUser(rs);
}
}
}
// BAD: Dynamic table name
public List<Object> getRecords(String tableName) throws SQLException {
String sql = "SELECT * FROM " + tableName; // Coverity: SQL_INJECTION
// ... execution
}
// GOOD: Validate table name
public List<Object> getRecordsFixed(String tableName) throws SQLException {
if (!isValidTableName(tableName)) {
throw new IllegalArgumentException("Invalid table name: " + tableName);
}
String sql = "SELECT * FROM " + tableName;
// ... execution
}
private boolean isValidTableName(String tableName) {
return tableName.matches("[a-zA-Z_][a-zA-Z0-9_]*");
}
}
4. Insecure Randomness
// COVERITY ISSUE: WEAK_RANDOM
public class RandomnessExample {
// BAD: Using java.util.Random for security purposes
public String generateAuthToken() {
Random random = new Random(); // Coverity: WEAK_RANDOM
return Long.toHexString(random.nextLong());
}
// GOOD: Use SecureRandom for cryptographic operations
public String generateAuthTokenFixed() {
SecureRandom secureRandom = new SecureRandom();
byte[] bytes = new byte[16];
secureRandom.nextBytes(bytes);
return Hex.encodeHexString(bytes);
}
// BAD: Predictable seed
public int generateSecurityCode() {
Random random = new Random(System.currentTimeMillis()); // Coverity: WEAK_RANDOM
return 1000 + random.nextInt(9000);
}
// GOOD: Let SecureRandom self-seed
public int generateSecurityCodeFixed() {
SecureRandom secureRandom = new SecureRandom();
return 1000 + secureRandom.nextInt(9000);
}
}
5. Path Manipulation
// COVERITY ISSUE: PATH_MANIPULATION
public class PathManipulationExample {
// BAD: User input used in file path
public File getUserFile(String username) {
String filePath = "/data/users/" + username + ".json"; // Coverity: PATH_MANIPULATION
return new File(filePath);
}
// GOOD: Validate and sanitize input
public File getUserFileFixed(String username) {
if (!isValidUsername(username)) {
throw new IllegalArgumentException("Invalid username: " + username);
}
String sanitized = username.replaceAll("[^a-zA-Z0-9_-]", "");
String filePath = "/data/users/" + sanitized + ".json";
return new File(filePath);
}
// BAD: Direct file system access with user input
public void deleteUserFile(String filename) throws IOException {
Files.delete(Paths.get("/data/" + filename)); // Coverity: PATH_MANIPULATION
}
// GOOD: Restrict to safe directory
public void deleteUserFileFixed(String filename) throws IOException {
Path baseDir = Paths.get("/data/safe");
Path resolved = baseDir.resolve(filename).normalize();
if (!resolved.startsWith(baseDir)) {
throw new SecurityException("Path traversal attempt: " + filename);
}
Files.delete(resolved);
}
private boolean isValidUsername(String username) {
return username != null && username.matches("[a-zA-Z0-9_-]{3,20}");
}
}
6. Command Injection
// COVERITY ISSUE: COMMAND_INJECTION
public class CommandInjectionExample {
// BAD: User input in system command
public void pingHost(String hostname) throws IOException {
String command = "ping -c 4 " + hostname; // Coverity: COMMAND_INJECTION
Runtime.getRuntime().exec(command);
}
// GOOD: Use ProcessBuilder with arguments
public void pingHostFixed(String hostname) throws IOException {
if (!isValidHostname(hostname)) {
throw new IllegalArgumentException("Invalid hostname: " + hostname);
}
ProcessBuilder pb = new ProcessBuilder("ping", "-c", "4", hostname);
Process process = pb.start();
// ... handle process
}
// BAD: Shell execution with user input
public void executeScript(String username) throws IOException {
String command = "sh user-script.sh " + username; // Coverity: COMMAND_INJECTION
Runtime.getRuntime().exec(command);
}
// GOOD: Separate command and arguments
public void executeScriptFixed(String username) throws IOException {
if (!isValidUsername(username)) {
throw new IllegalArgumentException("Invalid username: " + username);
}
ProcessBuilder pb = new ProcessBuilder("sh", "user-script.sh", username);
Process process = pb.start();
// ... handle process
}
private boolean isValidHostname(String hostname) {
return hostname != null && hostname.matches("^[a-zA-Z0-9.-]+$");
}
}
7. Cross-Site Scripting (XSS)
// COVERITY ISSUE: XSS
public class XssExample {
// BAD: Unsanitized user input in HTML
public String generateWelcomeMessage(String username) {
return "<h1>Welcome " + username + "!</h1>"; // Coverity: XSS
}
// GOOD: Escape HTML characters
public String generateWelcomeMessageFixed(String username) {
String safeUsername = escapeHtml(username);
return "<h1>Welcome " + safeUsername + "!</h1>";
}
// BAD: User input in JavaScript
public String generateUserScript(String userData) {
return "<script>var userData = '" + userData + "';</script>"; // Coverity: XSS
}
// GOOD: Use JSON and proper escaping
public String generateUserScriptFixed(String userData) {
String safeData = escapeJavaScript(userData);
return "<script>var userData = '" + safeData + "';</script>";
}
private String escapeHtml(String input) {
if (input == null) return "";
return input.replace("&", "&")
.replace("<", "<")
.replace(">", ">")
.replace("\"", """)
.replace("'", "'");
}
private String escapeJavaScript(String input) {
if (input == null) return "";
return input.replace("\\", "\\\\")
.replace("'", "\\'")
.replace("\"", "\\\"")
.replace("\n", "\\n")
.replace("\r", "\\r")
.replace("\t", "\\t");
}
}
8. Deadlock and Concurrency Issues
// COVERITY ISSUE: DEADLOCK, LOCK_ORDER
public class ConcurrencyExample {
private final Object lock1 = new Object();
private final Object lock2 = new Object();
// BAD: Potential deadlock
public void transferMoney(Account from, Account to, BigDecimal amount) {
synchronized (from) { // Coverity: DEADLOCK
synchronized (to) {
from.withdraw(amount);
to.deposit(amount);
}
}
}
// GOOD: Consistent lock ordering
public void transferMoneyFixed(Account from, Account to, BigDecimal amount) {
Object firstLock = from.getId() < to.getId() ? from : to;
Object secondLock = from.getId() < to.getId() ? to : from;
synchronized (firstLock) {
synchronized (secondLock) {
from.withdraw(amount);
to.deposit(amount);
}
}
}
// BAD: Nested synchronized methods
public class BankAccount {
public synchronized void transfer(BankAccount other, BigDecimal amount) {
// Coverity: DEADLOCK
this.withdraw(amount);
other.deposit(amount); // Acquires lock on 'other' while holding lock on 'this'
}
public synchronized void withdraw(BigDecimal amount) { /* ... */ }
public synchronized void deposit(BigDecimal amount) { /* ... */ }
}
// GOOD: Use explicit locks with timeout
public class BankAccountFixed {
private final Lock lock = new ReentrantLock();
public boolean transfer(BankAccountFixed other, BigDecimal amount, long timeout, TimeUnit unit) {
try {
if (this.lock.tryLock(timeout, unit)) {
try {
if (other.lock.tryLock(timeout, unit)) {
try {
this.withdraw(amount);
other.deposit(amount);
return true;
} finally {
other.lock.unlock();
}
}
} finally {
this.lock.unlock();
}
}
return false;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
}
}
Coverity Integration with CI/CD
1. Jenkins Pipeline
pipeline {
agent any
tools {
coverity 'coverity-2023.12'
}
stages {
stage('Build') {
steps {
sh 'mvn clean compile'
}
}
stage('Coverity Analysis') {
steps {
coverity(
coverityToolName: 'coverity-2023.12',
projectName: 'your-project',
streamName: 'your-stream',
covBuild: true,
covAnalyze: true,
covCommit: true,
failOnViolation: true,
violationFilters: [[
checkName: 'NULL_RETURNS',
maxSeverity: 'HIGH'
]]
)
}
}
}
post {
always {
coverityQualityGate(
projectId: 'your-project',
failureThreshold: 'HIGH'
)
}
}
}
2. GitHub Actions
name: Coverity Analysis
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
coverity:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
- name: Download Coverity
run: |
wget -q https://scan.coverity.com/download/linux64 \
--post-data "token=${{ secrets.COVERITY_TOKEN }}&project=your-project" \
-O coverity.tgz
tar xzf coverity.tgz
- name: Build with Coverity
run: |
export PATH=$(pwd)/cov-analysis-linux64-2023.12.0/bin:$PATH
cov-build --dir cov-int mvn compile -q
- name: Run Coverity Analysis
run: |
cov-analyze --dir cov-int --all --enable-constraint-fpp \
--enable-fnptr --enable-virtual
- name: Upload Results
run: |
tar czf coverity-results.tgz cov-int
curl --form token=${{ secrets.COVERITY_TOKEN }} \
--form [email protected] \
--form [email protected] \
--form version="$(git rev-parse --short HEAD)" \
--form description="GitHub Actions Build" \
https://scan.coverity.com/builds?project=your-project
3. GitLab CI
stages: - build - coverity coverity_scan: stage: coverity image: maven:3.8-openjdk-11 before_script: - apt-get update && apt-get install -y wget - wget -q https://scan.coverity.com/download/linux64 \ --post-data "token=$COVERITY_TOKEN&project=your-project" \ -O coverity.tgz - tar xzf coverity.tgz - export PATH=$(pwd)/cov-analysis-linux64-2023.12.0/bin:$PATH script: - cov-build --dir cov-int mvn compile -q - cov-analyze --dir cov-int --all - tar czf coverity-results.tgz cov-int - curl --form token=$COVERITY_TOKEN \ --form email="[email protected]" \ --form [email protected] \ --form version="$CI_COMMIT_SHA" \ --form description="GitLab CI Build" \ https://scan.coverity.com/builds?project=your-project only: - main - develop variables: COVERITY_TOKEN: $COVERITY_SCAN_TOKEN
Advanced Coverity Configuration
1. Custom Checker Configuration
<!-- coverity-checkers.xml --> <checker-configuration> <checker name="NULL_RETURNS"> <enabled>true</enabled> <severity>high</severity> </checker> <checker name="RESOURCE_LEAK"> <enabled>true</enabled> <severity>high</severity> </checker> <checker name="SQL_INJECTION"> <enabled>true</enabled> <severity>high</severity> </checker> <checker name="XSS"> <enabled>true</enabled> <severity>high</severity> </checker> <checker name="DEADLOCK"> <enabled>true</enabled> <severity>medium</severity> </checker> <!-- Disable less critical checkers --> <checker name="MISSING_BREAK"> <enabled>false</enabled> </checker> <checker name="UNUSED_VALUE"> <enabled>false</enabled> </checker> </checker-configuration>
2. Model Configuration for Frameworks
{
"models": [
{
"name": "SpringFramework",
"version": "5.3.0",
"methods": [
{
"name": "org.springframework.jdbc.core.JdbcTemplate.queryForObject",
"returns_null": "never"
},
{
"name": "org.springframework.web.bind.annotation.RequestParam",
"taint_source": "true"
}
]
},
{
"name": "Hibernate",
"version": "5.6.0",
"methods": [
{
"name": "org.hibernate.Session.get",
"returns_null": "sometimes"
},
{
"name": "org.hibernate.query.Query.uniqueResult",
"returns_null": "sometimes"
}
]
}
]
}
3. Suppression Configuration
// Coverity suppression annotations
public class SuppressionExamples {
// Suppress specific issue on method
@CoveritySuppress("NULL_RETURNS")
public String legacyMethodThatReturnsNull() {
// This method is known to return null in some cases
return null;
}
// Suppress with justification
@CoveritySuppress(value = "RESOURCE_LEAK", justification = "Resource managed by container")
public DataSource getDataSource() {
// Application server manages the DataSource lifecycle
return dataSource;
}
// Suppress on field
@CoveritySuppress("FB.SECURITY")
private String internalApiKey; // False positive - key is encrypted
// Suppress in specific block
public void processData() {
// Some code...
// coverity[FORWARD_NULL]
String result = potentiallyNullString.toString();
// More code...
}
}
4. Custom Triage Rules
{
"triage_rules": [
{
"rule_name": "Ignore test files",
"file_pattern": "**/test/**",
"action": "suppress"
},
{
"rule_name": "Third party code",
"file_pattern": "**/thirdparty/**",
"action": "suppress"
},
{
"rule_name": "High priority security",
"checker_name": ["SQL_INJECTION", "XSS", "PATH_MANIPULATION"],
"severity": "high",
"action": "require_review"
},
{
"rule_name": "Ignore legacy code",
"file_pattern": "**/legacy/**",
"checker_name": ["NULL_RETURNS", "RESOURCE_LEAK"],
"action": "ignore"
}
]
}
Best Practices for Coverity Integration
1. Development Workflow
public class CoverityWorkflow {
// 1. Run Coverity locally before committing
public void preCommitCheck() {
// Developers should run:
// cov-build --dir cov-int mvn compile
// cov-analyze --dir cov-int
// cov-format-errors --dir cov-int
}
// 2. Fix high-severity issues immediately
public void handleCriticalIssues() {
// NULL_RETURNS, RESOURCE_LEAK, SQL_INJECTION
// should be fixed before merge
}
// 3. Review medium-severity issues weekly
public void weeklyReview() {
// DEADLOCK, XSS, PATH_MANIPULATION
// should be reviewed and prioritized
}
// 4. Monitor security issues daily
public void securityMonitoring() {
// SQL_INJECTION, XSS, COMMAND_INJECTION
// should trigger immediate alerts
}
}
2. Quality Gates
public class CoverityQualityGates {
// Quality gate criteria
public boolean passesQualityGate(CoverityReport report) {
return report.getHighSeverityCount() == 0 &&
report.getNewIssuesCount() <= 5 &&
report.getSecurityIssueCount() == 0 &&
report.getFixedIssueCount() >= report.getNewIssuesCount();
}
// Security-specific gate
public boolean passesSecurityGate(CoverityReport report) {
return report.getSqlInjectionCount() == 0 &&
report.getXssCount() == 0 &&
report.getPathManipulationCount() == 0 &&
report.getCommandInjectionCount() == 0;
}
}
3. Team Training and Adoption
public class CoverityAdoption {
// Phase 1: Awareness
public void initialSetup() {
// Install Coverity
// Configure basic rules
// Run initial analysis
// Share results with team
}
// Phase 2: Integration
public void ciIntegration() {
// Add to CI pipeline
// Set up quality gates
// Configure notifications
// Train developers on fixing issues
}
// Phase 3: Optimization
public void advancedUsage() {
// Custom checkers
// Model configurations
// Triage rules
// Advanced reporting
}
// Phase 4: Continuous Improvement
public void ongoingMaintenance() {
// Regular rule reviews
// Performance optimization
// Integration with other tools
// Metrics and reporting
}
}
Troubleshooting Common Issues
1. Build Integration Problems
# Common issues and solutions # Issue: Coverity can't find source files # Solution: Ensure proper build configuration cov-build --dir cov-int mvn clean compile # Issue: Memory errors during analysis # Solution: Increase heap size cov-analyze --dir cov-int --java-jvm-args "-Xmx8G -Xms4G" # Issue: False positives # Solution: Use models and suppression cov-analyze --dir cov-int --model-file custom-models.json # Issue: Slow analysis # Solution: Use incremental analysis cov-analyze --dir cov-int --incremental # Issue: Missing dependencies # Solution: Include all dependencies in classpath cov-build --dir cov-int --java-classpath "target/classes:target/dependency/*" mvn compile
2. Analysis Configuration
// Optimal analysis configuration
public class CoverityAnalysisConfig {
public void configureAnalysis() {
// Use constraint-based analysis for better precision
// cov-analyze --enable-constraint-fpp
// Enable function pointer analysis
// cov-analyze --enable-fnptr
// Enable virtual function analysis
// cov-analyze --enable-virtual
// Set appropriate memory limits
// cov-analyze --java-jvm-args "-Xmx8G -Xms4G"
// Use multiple cores for faster analysis
// cov-analyze --jobs 4
}
}
Conclusion
Coverity integration provides:
- Comprehensive security analysis for Java applications
- Early defect detection in the development lifecycle
- Quality metrics and compliance reporting
- CI/CD integration for continuous quality assurance
- Customizable rule sets for project-specific requirements
By implementing the patterns and configurations shown above, you can significantly improve your Java application's security, reliability, and maintainability while establishing a robust static analysis practice within your development workflow.