Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions .github/codeql/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# CodeQL Security Analysis for Unraid API

This directory contains custom CodeQL queries and configurations for security analysis of the Unraid API codebase.

## Overview

The analysis is configured to run:
- On all pushes to the main branch
- On all pull requests
- Weekly via scheduled runs

## Custom Queries

The following custom queries are implemented:

1. **API Authorization Bypass Detection**
Identifies API handlers that may not properly check authorization before performing operations.

2. **GraphQL Injection Detection**
Detects potential injection vulnerabilities in GraphQL queries and operations.

3. **Hardcoded Secrets Detection**
Finds potential hardcoded secrets or credentials in the codebase.

4. **Insecure Cryptographic Implementations**
Identifies usage of weak cryptographic algorithms or insecure random number generation.

5. **Path Traversal Vulnerability Detection**
Detects potential path traversal vulnerabilities in file system operations.

## Configuration

The CodeQL analysis is configured in:
- `.github/workflows/codeql-analysis.yml` - Workflow configuration
- `.github/codeql/codeql-config.yml` - CodeQL engine configuration

## Running Locally

To run these queries locally:

1. Install the CodeQL CLI: https://github.com/github/codeql-cli-binaries/releases
2. Create a CodeQL database:
```
codeql database create <db-name> --language=javascript --source-root=.
```
3. Run a query:
```
codeql query run .github/codeql/custom-queries/javascript/api-auth-bypass.ql --database=<db-name>
```
16 changes: 16 additions & 0 deletions .github/codeql/codeql-config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: "Unraid API CodeQL Configuration"

disable-default-queries: false

queries:
- name: Extended Security Queries
uses: security-extended
- name: Custom Unraid API Queries
uses: ./.github/codeql/custom-queries

query-filters:
- exclude:
problem.severity:
- warning
- recommendation
tags contain: security
45 changes: 45 additions & 0 deletions .github/codeql/custom-queries/javascript/api-auth-bypass.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* @name Potential API Authorization Bypass
* @description Functions that process API requests without verifying authorization may lead to security vulnerabilities.
* @kind problem
* @problem.severity error
* @precision medium
* @id js/api-auth-bypass
* @tags security
* external/cwe/cwe-285
*/

import javascript

/**
* Identifies functions that appear to handle API requests
*/
predicate isApiHandler(Function f) {
exists(f.getAParameter()) and
(
f.getName().regexpMatch("(?i).*(api|handler|controller|resolver|endpoint).*") or
exists(CallExpr call |
call.getCalleeName().regexpMatch("(?i).*(get|post|put|delete|patch).*") and
call.getArgument(1) = f
)
)
}

/**
* Identifies expressions that appear to perform authorization checks
*/
predicate isAuthCheck(DataFlow::Node node) {
exists(CallExpr call |
call.getCalleeName().regexpMatch("(?i).*(authorize|authenticate|isAuth|checkAuth|verifyAuth|hasPermission|isAdmin|canAccess).*") and
call.flow().getASuccessor*() = node
)
}

from Function apiHandler
where
isApiHandler(apiHandler) and
not exists(DataFlow::Node authCheck |
isAuthCheck(authCheck) and
authCheck.getEnclosingExpr().getEnclosingFunction() = apiHandler
)
select apiHandler, "API handler function may not perform proper authorization checks."
77 changes: 77 additions & 0 deletions .github/codeql/custom-queries/javascript/graphql-injection.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* @name Potential GraphQL Injection
* @description User-controlled input used directly in GraphQL queries may lead to injection vulnerabilities.
* @kind path-problem
* @problem.severity error
* @precision high
* @id js/graphql-injection
* @tags security
* external/cwe/cwe-943
*/

import javascript
import DataFlow::PathGraph

class GraphQLQueryExecution extends DataFlow::CallNode {
GraphQLQueryExecution() {
exists(string name |
name = this.getCalleeName() and
(
name = "execute" or
name = "executeQuery" or
name = "query" or
name.regexpMatch("(?i).*graphql.*query.*")
)
)
}

DataFlow::Node getQuery() {
result = this.getArgument(0)
}
}

class UserControlledInput extends DataFlow::Node {
UserControlledInput() {
exists(DataFlow::ParameterNode param |
param.getName().regexpMatch("(?i).*(query|request|input|args|variables|params).*") and
this = param
)
or
exists(DataFlow::PropRead prop |
prop.getPropertyName().regexpMatch("(?i).*(query|request|input|args|variables|params).*") and
this = prop
)
}
}

/**
* Holds if `node` is a string concatenation.
*/
predicate isStringConcatenation(DataFlow::Node node) {
exists(BinaryExpr concat |
concat.getOperator() = "+" and
concat.flow() = node
)
}

class GraphQLInjectionConfig extends TaintTracking::Configuration {
GraphQLInjectionConfig() { this = "GraphQLInjectionConfig" }

override predicate isSource(DataFlow::Node source) {
source instanceof UserControlledInput
}

override predicate isSink(DataFlow::Node sink) {
exists(GraphQLQueryExecution exec | sink = exec.getQuery())
}

override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
// Add any GraphQL-specific taint steps if needed
isStringConcatenation(succ) and
succ.(DataFlow::BinaryExprNode).getAnOperand() = pred
}
}

from GraphQLInjectionConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "GraphQL query may contain user-controlled input from $@.", source.getNode(), "user input"
53 changes: 53 additions & 0 deletions .github/codeql/custom-queries/javascript/hardcoded-secrets.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* @name Hardcoded Secrets
* @description Hardcoded secrets or credentials in source code can lead to security vulnerabilities.
* @kind problem
* @problem.severity error
* @precision medium
* @id js/hardcoded-secrets
* @tags security
* external/cwe/cwe-798
*/

import javascript

/**
* Identifies variable declarations or assignments that may contain secrets
*/
predicate isSensitiveAssignment(DataFlow::Node node) {
exists(DataFlow::PropWrite propWrite |
propWrite.getPropertyName().regexpMatch("(?i).*(secret|key|password|token|credential|auth).*") and
propWrite.getRhs() = node
)
or
exists(VariableDeclarator decl |
decl.getName().regexpMatch("(?i).*(secret|key|password|token|credential|auth).*") and
decl.getInit().flow() = node
)
}

/**
* Identifies literals that look like secrets
*/
predicate isSecretLiteral(StringLiteral literal) {
// Match alphanumeric strings of moderate length that may be secrets
literal.getValue().regexpMatch("[A-Za-z0-9_\\-]{8,}") and

not (
// Skip likely non-sensitive literals
literal.getValue().regexpMatch("(?i)^(true|false|null|undefined|localhost|development|production|staging)$") or
// Skip URLs without credentials
literal.getValue().regexpMatch("^https?://[^:@/]+")
)
}

from DataFlow::Node source
where
isSensitiveAssignment(source) and
(
exists(StringLiteral literal |
literal.flow() = source and
isSecretLiteral(literal)
)
)
select source, "This assignment may contain a hardcoded secret or credential."
90 changes: 90 additions & 0 deletions .github/codeql/custom-queries/javascript/insecure-crypto.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/**
* @name Insecure Cryptographic Implementation
* @description Usage of weak cryptographic algorithms or improper implementations can lead to security vulnerabilities.
* @kind problem
* @problem.severity error
* @precision high
* @id js/insecure-crypto
* @tags security
* external/cwe/cwe-327
*/

import javascript

/**
* Identifies calls to crypto functions with insecure algorithms
*/
predicate isInsecureCryptoCall(CallExpr call) {
// Node.js crypto module uses
exists(string methodName |
methodName = call.getCalleeName() and
(
// Detect MD5 usage
methodName.regexpMatch("(?i).*md5.*") or
methodName.regexpMatch("(?i).*sha1.*") or

// Insecure crypto constructors
(
methodName = "createHash" or
methodName = "createCipheriv" or
methodName = "createDecipher"
) and
(
exists(StringLiteral algo |
algo = call.getArgument(0) and
(
algo.getValue().regexpMatch("(?i).*(md5|md4|md2|sha1|des|rc4|blowfish).*") or
algo.getValue().regexpMatch("(?i).*(ecb).*") // ECB mode
)
)
)
)
)
or
// Browser crypto API uses
exists(MethodCallExpr mce, string propertyName |
propertyName = mce.getMethodName() and
(
propertyName = "subtle" and
exists(MethodCallExpr subtleCall |
subtleCall.getReceiver() = mce and
subtleCall.getMethodName() = "encrypt" and
exists(ObjectExpr obj |
obj = subtleCall.getArgument(0) and
exists(Property p |
p = obj.getAProperty() and
p.getName() = "name" and
exists(StringLiteral algo |
algo = p.getInit() and
algo.getValue().regexpMatch("(?i).*(rc4|des|aes-cbc).*")
)
)
)
)
)
)
}

/**
* Identifies usage of Math.random() for security-sensitive operations
*/
predicate isInsecureRandomCall(CallExpr call) {
exists(PropertyAccess prop |
prop.getPropertyName() = "random" and
prop.getBase().toString() = "Math" and
call.getCallee() = prop
)
}

from Expr insecureExpr, string message
where
(
insecureExpr instanceof CallExpr and
isInsecureCryptoCall(insecureExpr) and
message = "Using potentially insecure cryptographic algorithm or mode."
) or (
insecureExpr instanceof CallExpr and
isInsecureRandomCall(insecureExpr) and
message = "Using Math.random() for security-sensitive operation. Consider using crypto.getRandomValues() instead."
)
select insecureExpr, message
Loading
Loading