vibe-guard
Version:
██ Vibe-Guard Security Scanner - 28 essential security rules to catch vulnerabilities before they catch you! Zero dependencies, instant setup, works everywhere, optimized performance. Detects SQL injection, XSS, exposed secrets, CSRF, CORS issues, contain
314 lines • 18.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.KubernetesSecurityRule = void 0;
const types_1 = require("../types");
class KubernetesSecurityRule extends types_1.BaseRule {
constructor() {
super(...arguments);
this.name = 'kubernetes-security';
this.description = 'Detects Kubernetes security misconfigurations and vulnerabilities';
this.severity = 'high';
this.kubernetesPatterns = [
{
pattern: /privileged:\s*true/gm,
type: 'Privileged Container',
severity: 'critical',
description: 'Container running in privileged mode has full host access',
suggestion: 'Remove privileged: true and use specific capabilities if needed'
},
{
pattern: /hostNetwork:\s*true/gm,
type: 'Host Namespace Sharing',
severity: 'high',
description: 'Container shares host network namespace - breaks isolation',
suggestion: 'Use hostNetwork: false and configure proper network policies'
},
{
pattern: /hostPID:\s*true/gm,
type: 'Host Namespace Sharing',
severity: 'high',
description: 'Container shares host PID namespace - breaks isolation',
suggestion: 'Use hostPID: false to isolate process namespace'
},
{
pattern: /hostIPC:\s*true/gm,
type: 'Host Namespace Sharing',
severity: 'high',
description: 'Container shares host IPC namespace - breaks isolation',
suggestion: 'Use hostIPC: false to isolate IPC namespace'
},
{
pattern: /runAsUser:\s*0/gm,
type: 'Root User Execution',
severity: 'critical',
description: 'Container running as root user (UID 0)',
suggestion: 'Set runAsUser to non-zero value (e.g., 1000)'
},
{
pattern: /runAsNonRoot:\s*false/gm,
type: 'Non-Root Disabled',
severity: 'critical',
description: 'Non-root user enforcement explicitly disabled',
suggestion: 'Set runAsNonRoot: true to enforce non-root execution'
},
{
pattern: /readOnlyRootFilesystem:\s*false/gm,
type: 'Writable Root Filesystem',
severity: 'medium',
description: 'Container has writable root filesystem - allows tampering',
suggestion: 'Set readOnlyRootFilesystem: true and use emptyDir volumes for writable data'
},
{
pattern: /allowPrivilegeEscalation:\s*true/gm,
type: 'Privilege Escalation Allowed',
severity: 'high',
description: 'Container can escalate privileges',
suggestion: 'Set allowPrivilegeEscalation: false'
},
{
pattern: /add:\s*\[[^\]]*["\']SYS_ADMIN["\'][^\]]*\]/gm,
type: 'Dangerous Capabilities',
severity: 'critical',
description: 'Container has SYS_ADMIN capability - dangerous kernel-level access',
suggestion: 'Remove SYS_ADMIN capability and use minimal required capabilities'
},
{
pattern: /add:\s*\[[^\]]*["\']NET_ADMIN["\'][^\]]*\]/gm,
type: 'Dangerous Capabilities',
severity: 'high',
description: 'Container has NET_ADMIN capability - dangerous kernel-level access',
suggestion: 'Remove NET_ADMIN capability unless absolutely necessary'
},
{
pattern: /add:\s*\[[^\]]*["\'](?:SYS_TIME|SYS_MODULE|SYS_RAWIO|SYS_PTRACE)["\'][^\]]*\]/gm,
type: 'Dangerous Capabilities',
severity: 'high',
description: 'Container has dangerous capabilities - kernel-level access',
suggestion: 'Remove dangerous capabilities and use minimal required capabilities'
},
{
pattern: /image:\s*([^:]+):latest/gm,
type: 'Latest Image Tag',
severity: 'medium',
description: 'Using latest image tag can lead to unpredictable deployments',
suggestion: 'Use specific version tags (e.g., nginx:1.21.6 instead of nginx:latest)'
},
{
pattern: /imagePullPolicy:\s*Always/gm,
type: 'Always Pull Policy',
severity: 'low',
description: 'Always pulling images increases deployment overhead and security risks',
suggestion: 'Use IfNotPresent or specific version tags with IfNotPresent policy'
},
{
pattern: /automountServiceAccountToken:\s*true/gm,
type: 'Service Account Token Mounting',
severity: 'medium',
description: 'Service account token automatically mounted - potential privilege escalation',
suggestion: 'Set automountServiceAccountToken: false unless explicitly needed'
},
{
pattern: /^\s*path:\s*\/var\/lib/gm,
type: 'Host Path Volume',
severity: 'high',
description: 'Host path volume mounted - potential host filesystem access',
suggestion: 'Avoid hostPath volumes or use read-only mounts with specific paths'
}
];
}
check(fileContent) {
const issues = [];
if (!this.isKubernetesManifest(fileContent.path)) {
return issues;
}
const context = this.analyzeKubernetesContext(fileContent.content);
for (const { pattern, type, severity, description, suggestion } of this.kubernetesPatterns) {
const matches = this.findMatches(fileContent.content, pattern);
for (const { line, column, lineContent } of matches) {
if (this.shouldSkipMatch(type, context, lineContent, fileContent.content)) {
continue;
}
const finalSeverity = this.determineSeverity(severity, context, type);
const enhancedSuggestion = this.enhanceSuggestion(suggestion, context, type);
issues.push(this.createIssue(fileContent.path, line, column, lineContent, `${type}: ${description}`, enhancedSuggestion, finalSeverity));
}
}
const missingPractices = this.checkMissingPractices(context);
issues.push(...missingPractices);
return issues;
}
isKubernetesManifest(filePath) {
const lowerPath = filePath.toLowerCase();
return lowerPath.includes('k8s') ||
lowerPath.includes('kubernetes') ||
lowerPath.includes('deployment') ||
lowerPath.includes('service') ||
lowerPath.includes('pod') ||
lowerPath.includes('namespace') ||
(lowerPath.endsWith('.yaml') && (lowerPath.includes('k8s') || lowerPath.includes('kubernetes'))) ||
(lowerPath.endsWith('.yml') && (lowerPath.includes('k8s') || lowerPath.includes('kubernetes')));
}
analyzeKubernetesContext(content) {
return {
hasSecurityContext: /securityContext:/m.test(content),
hasResourceLimits: /limits:/m.test(content),
hasResourceRequests: /requests:/m.test(content),
hasServiceAccount: /serviceAccount:/m.test(content),
hasNonRootUser: /runAsNonRoot:\s*true/m.test(content),
hasReadOnlyRootFilesystem: /readOnlyRootFilesystem:\s*true/m.test(content),
hasPrivilegedMode: /privileged:\s*true/m.test(content),
hasHostNetwork: /hostNetwork:\s*true/m.test(content),
hasHostPID: /hostPID:\s*true/m.test(content),
hasHostIPC: /hostIPC:\s*true/m.test(content),
hasCapabilities: /capabilities:/m.test(content),
hasNetworkPolicies: /kind:\s*NetworkPolicy/m.test(content),
hasPodSecurityPolicy: /kind:\s*PodSecurityPolicy/m.test(content),
hasRBAC: /kind:\s*(Role|ClusterRole|RoleBinding|ClusterRoleBinding)/m.test(content),
resourceType: this.extractResourceType(content),
namespace: this.extractNamespace(content),
hasImagePullSecrets: /imagePullSecrets:/m.test(content),
hasImagePullPolicy: /imagePullPolicy:/m.test(content),
hasAutomountServiceAccountToken: /automountServiceAccountToken:/m.test(content),
hasHostPathVolumes: /hostPath:/m.test(content),
hasSeccompProfile: /seccompProfile:/m.test(content),
hasAppArmorProfile: /appArmorProfile:/m.test(content),
hasSELinuxOptions: /seLinuxOptions:/m.test(content),
hasReadinessProbe: /readinessProbe:/m.test(content),
hasLivenessProbe: /livenessProbe:/m.test(content),
hasStartupProbe: /startupProbe:/m.test(content),
hasResourceQuotas: /kind:\s*ResourceQuota/m.test(content),
hasLimitRanges: /kind:\s*LimitRange/m.test(content),
hasPodDisruptionBudget: /kind:\s*PodDisruptionBudget/m.test(content)
};
}
extractResourceType(content) {
const kindMatch = content.match(/kind:\s*(\w+)/m);
return kindMatch?.[1] ?? 'unknown';
}
extractNamespace(content) {
const namespaceMatch = content.match(/namespace:\s*(\w+)/m);
return namespaceMatch?.[1] ?? 'default';
}
shouldSkipMatch(type, context, lineContent, fullContent) {
if (type === 'Root User Execution') {
const lines = fullContent.split('\n');
const currentLineIndex = lines.findIndex(line => line.trim() === lineContent.trim());
if (currentLineIndex >= 0) {
let securityContextStart = -1;
for (let i = currentLineIndex; i >= 0; i--) {
if (lines[i]?.includes('securityContext:')) {
securityContextStart = i;
break;
}
}
let securityContextEnd = lines.length;
if (securityContextStart >= 0) {
for (let i = securityContextStart + 1; i < lines.length; i++) {
const line = lines[i];
if (line && line.trim() && !line.startsWith(' ') && !line.startsWith('\t')) {
securityContextEnd = i;
break;
}
}
}
if (securityContextStart >= 0 && securityContextEnd > securityContextStart) {
const securityContextBlock = lines.slice(securityContextStart, securityContextEnd).join('\n');
if (securityContextBlock.includes('runAsNonRoot: true')) {
return true;
}
}
}
}
if (type === 'Privilege Escalation Allowed' && context.hasSecurityContext) {
return false;
}
return false;
}
determineSeverity(baseSeverity, context, type) {
if (type === 'Privileged Container' && context.hasPrivilegedMode) {
return 'critical';
}
if (type === 'Root User Execution' && !context.hasNonRootUser) {
return 'critical';
}
if (type === 'Dangerous Capabilities' && context.hasCapabilities) {
return 'critical';
}
if (type === 'Host Namespace Sharing' && (context.hasHostNetwork || context.hasHostPID || context.hasHostIPC)) {
return 'high';
}
if (type === 'Privilege Escalation Allowed' && !context.hasSecurityContext) {
return 'high';
}
if (type === 'Non-Root Disabled' && !context.hasNonRootUser) {
return 'critical';
}
if (type === 'Host Path Volume' && context.hasHostPathVolumes) {
return 'high';
}
return baseSeverity;
}
enhanceSuggestion(baseSuggestion, context, type) {
let suggestion = baseSuggestion;
if (type === 'Root User Execution') {
suggestion += '\n\n**Complete secure security context example:**\n```yaml\nsecurityContext:\n runAsNonRoot: true\n runAsUser: 1000\n runAsGroup: 1000\n readOnlyRootFilesystem: true\n allowPrivilegeEscalation: false\n capabilities:\n drop: ["ALL"]\n```';
}
if (type === 'Privileged Container') {
suggestion += '\n\n**Alternative approach with specific capabilities:**\n```yaml\nsecurityContext:\n capabilities:\n add: ["NET_BIND_SERVICE"] # Only add specific capabilities needed\n drop: ["ALL"]\n runAsNonRoot: true\n readOnlyRootFilesystem: true\n```';
}
if (type === 'Host Namespace Sharing') {
suggestion += '\n\n**Secure namespace isolation:**\n```yaml\nspec:\n hostNetwork: false\n hostPID: false\n hostIPC: false\n securityContext:\n runAsNonRoot: true\n runAsUser: 1000\n```';
}
if (type === 'Dangerous Capabilities') {
suggestion += '\n\n**Capabilities restriction example:**\n```yaml\nsecurityContext:\n capabilities:\n add: ["NET_BIND_SERVICE"] # Only add what you need\n drop: ["ALL"] # Drop all by default\n```';
}
if (type === 'Writable Root Filesystem') {
suggestion += '\n\n**Read-only filesystem with writable volumes:**\n```yaml\nsecurityContext:\n readOnlyRootFilesystem: true\nvolumes:\n- name: tmp-volume\n emptyDir: {}\n- name: cache-volume\n emptyDir: {}\n```';
}
if (type === 'Latest Image Tag') {
suggestion += `\n\n**Current resource type:** ${context.resourceType}`;
suggestion += '\n**Recommended:** Use specific version tags for reproducible deployments';
suggestion += '\n**Example:** Replace `nginx:latest` with `nginx:1.21.6`';
}
if (type === 'Service Account Token Mounting') {
suggestion += '\n\n**Disable automatic token mounting:**\n```yaml\nspec:\n automountServiceAccountToken: false\n serviceAccountName: my-service-account\n```';
}
if (type === 'Host Path Volume') {
suggestion += '\n\n**Secure volume alternatives:**\n```yaml\n# Use emptyDir instead of hostPath\nvolumes:\n- name: app-data\n emptyDir: {}\n\n# If hostPath is required, use read-only\n- name: host-data\n hostPath:\n path: /var/lib/app\n type: DirectoryOrCreate\n readOnly: true\n```';
}
return suggestion;
}
checkMissingPractices(context) {
const issues = [];
if (context.resourceType === 'unknown') {
return issues;
}
if (!context.hasSecurityContext) {
issues.push(this.createIssue('Kubernetes Manifest', 1, 1, 'apiVersion: ...', 'Missing security context: No security constraints defined - default insecure baseline', 'Add security context with non-root user and read-only filesystem:\n```yaml\nsecurityContext:\n runAsNonRoot: true\n runAsUser: 1000\n readOnlyRootFilesystem: true\n allowPrivilegeEscalation: false\n```', 'high'));
}
if (!context.hasResourceLimits) {
issues.push(this.createIssue('Kubernetes Manifest', 1, 1, 'apiVersion: ...', 'Missing resource limits: No resource constraints defined - risk of denial-of-service', 'Add resource limits to prevent resource exhaustion attacks:\n```yaml\nresources:\n requests:\n cpu: "100m"\n memory: "128Mi"\n limits:\n cpu: "500m"\n memory: "512Mi"\n```', 'medium'));
}
if (!context.hasServiceAccount && (context.resourceType === 'Pod' || context.resourceType === 'Deployment')) {
issues.push(this.createIssue('Kubernetes Manifest', 1, 1, 'apiVersion: ...', 'Missing service account: Using overly-permissive default service account', 'Create dedicated service account with minimal permissions:\n```yaml\nspec:\n serviceAccountName: my-service-account\n automountServiceAccountToken: false\n```', 'medium'));
}
if (!context.hasNetworkPolicies) {
issues.push(this.createIssue('Kubernetes Manifest', 1, 1, 'apiVersion: ...', 'Missing network policies: All pods have unrestricted network access', 'Implement network policies to restrict pod-to-pod communication:\n```yaml\nkind: NetworkPolicy\nspec:\n podSelector: {}\n policyTypes: ["Ingress", "Egress"]\n```', 'medium'));
}
if (!context.hasRBAC && (context.resourceType === 'Pod' || context.resourceType === 'Deployment')) {
issues.push(this.createIssue('Kubernetes Manifest', 1, 1, 'apiVersion: ...', 'Missing RBAC: No role-based access control enforcement', 'Implement RBAC with least privilege principle:\n```yaml\nkind: Role\nrules:\n- apiGroups: [""]\n resources: ["pods"]\n verbs: ["get", "list"]\n```', 'medium'));
}
if (!context.hasReadinessProbe && !context.hasLivenessProbe && (context.resourceType === 'Pod' || context.resourceType === 'Deployment')) {
issues.push(this.createIssue('Kubernetes Manifest', 1, 1, 'apiVersion: ...', 'Missing health checks: No readiness or liveness probes defined', 'Add health checks for better reliability:\n```yaml\nlivenessProbe:\n httpGet:\n path: /health\n port: 8080\n initialDelaySeconds: 30\nreadinessProbe:\n httpGet:\n path: /ready\n port: 8080\n```', 'low'));
}
if (!context.hasResourceQuotas && context.namespace !== 'default') {
issues.push(this.createIssue('Kubernetes Manifest', 1, 1, 'apiVersion: ...', 'Missing resource quotas: No namespace-level resource constraints', 'Implement resource quotas to prevent resource exhaustion:\n```yaml\nkind: ResourceQuota\nspec:\n hard:\n requests.cpu: "2"\n requests.memory: 4Gi\n limits.cpu: "4"\n limits.memory: 8Gi\n```', 'low'));
}
if (!context.hasLimitRanges && context.namespace !== 'default') {
issues.push(this.createIssue('Kubernetes Manifest', 1, 1, 'apiVersion: ...', 'Missing limit ranges: No default resource limits for pods', 'Implement limit ranges for default resource constraints:\n```yaml\nkind: LimitRange\nspec:\n limits:\n - default:\n cpu: "500m"\n memory: "512Mi"\n type: Container\n```', 'low'));
}
return issues;
}
}
exports.KubernetesSecurityRule = KubernetesSecurityRule;
//# sourceMappingURL=kubernetes-security.js.map