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
44 changes: 44 additions & 0 deletions rules/stellar/access-control/detect-missing-access-control.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Detect Missing Soroban Access Control Layers (#375)
* Flags privileged functions that expose sensitive operations without role validation.
*/

export interface AccessControlResult {
detected: boolean;
flaggedFunctions: string[];
message: string;
suggestion: string;
}

const PRIVILEGED_PATTERNS = [
/fn\s+(set_admin|transfer_admin|withdraw|pause|unpause|mint|burn|upgrade)\s*\(/g,
];

const AUTH_GUARD = /require_auth|only_admin|assert_owner|has_role/;

export function detectMissingAccessControl(code: string): AccessControlResult {
const flaggedFunctions: string[] = [];

for (const pattern of PRIVILEGED_PATTERNS) {
for (const match of code.matchAll(pattern)) {
const fnName = match[1];
// Extract a small window of code after the function signature to check for an auth guard
const startIdx = match.index ?? 0;
const window = code.slice(startIdx, startIdx + 300);
if (!AUTH_GUARD.test(window)) {
flaggedFunctions.push(fnName);
}
}
}

if (flaggedFunctions.length === 0) {
return { detected: false, flaggedFunctions: [], message: 'Access control present on all privileged functions.', suggestion: '' };
}

return {
detected: true,
flaggedFunctions,
message: `Privileged function(s) lack access control: ${flaggedFunctions.join(', ')}.`,
suggestion: 'Add `require_auth()` or an admin role check at the start of each privileged function.',
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Detect Unsafe Cross-Contract Invocation (#378)
* Flags cross-contract calls that lack caller validation or return-value checks.
*/

export interface CrossContractResult {
detected: boolean;
message: string;
suggestion: string;
}

const INVOCATION_PATTERNS = [
/invoke_contract\s*\(/,
/Client::new\s*\(/,
/env\.invoke\s*\(/,
/call_contract\s*\(/,
];

const VALIDATION_PATTERNS = [
/require_auth/,
/verify_contract/,
/\.unwrap_or_else/,
/\.expect\s*\(/,
/match\s+.*\{/,
];

export function detectUnsafeCrossContractInvocation(code: string): CrossContractResult {
const hasInvocation = INVOCATION_PATTERNS.some((p) => p.test(code));

if (!hasInvocation) {
return { detected: false, message: 'No cross-contract invocations found.', suggestion: '' };
}

const hasValidation = VALIDATION_PATTERNS.some((p) => p.test(code));

if (!hasValidation) {
return {
detected: true,
message: 'Cross-contract invocation detected without caller validation or result verification.',
suggestion: 'Validate the callee address and handle invocation results explicitly to prevent unexpected behaviour.',
};
}

return { detected: false, message: 'Cross-contract invocation appears guarded.', suggestion: '' };
}
36 changes: 36 additions & 0 deletions rules/stellar/optimization/detect-inefficient-symbol-usage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Detect Inefficient Symbol Usage (#374)
* Flags repeated inline Symbol construction that could be replaced with static references.
*/

export interface SymbolUsageResult {
detected: boolean;
repeated: string[];
message: string;
suggestion: string;
}

export function detectInefficientSymbolUsage(code: string): SymbolUsageResult {
const matches = [...code.matchAll(/Symbol::new\(\s*&env\s*,\s*["']([^"']+)["']\s*\)/g)];
const names = matches.map((m) => m[1]);

const counts: Record<string, number> = {};
for (const name of names) {
counts[name] = (counts[name] ?? 0) + 1;
}

const repeated = Object.entries(counts)
.filter(([, count]) => count > 1)
.map(([name]) => name);

if (repeated.length === 0) {
return { detected: false, repeated: [], message: 'No repeated symbol construction found.', suggestion: '' };
}

return {
detected: true,
repeated,
message: `Symbol(s) constructed multiple times: ${repeated.join(', ')}.`,
suggestion: 'Define symbols as constants or lazy statics to avoid repeated allocation overhead.',
};
}
33 changes: 33 additions & 0 deletions rules/stellar/upgradeability/detect-missing-upgrade-guards.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Detect Missing Contract Upgrade Guards (#373)
* Flags upgrade functions that lack admin/owner validation.
*/

export interface UpgradeGuardResult {
detected: boolean;
message: string;
suggestion: string;
}

const UPGRADE_PATTERNS = [/fn\s+upgrade\s*\(/, /fn\s+update_contract\s*\(/, /fn\s+set_wasm\s*\(/];
const AUTH_PATTERNS = [/require_auth/, /admin\.require_auth/, /only_admin/, /assert_admin/];

export function detectMissingUpgradeGuards(code: string): UpgradeGuardResult {
const hasUpgradeMethod = UPGRADE_PATTERNS.some((p) => p.test(code));

if (!hasUpgradeMethod) {
return { detected: false, message: 'No upgrade methods found.', suggestion: '' };
}

const hasAuthCheck = AUTH_PATTERNS.some((p) => p.test(code));

if (!hasAuthCheck) {
return {
detected: true,
message: 'Upgrade method detected without admin authorization guard.',
suggestion: 'Add `admin.require_auth()` or an equivalent role check before allowing contract upgrades.',
};
}

return { detected: false, message: 'Upgrade guard present.', suggestion: '' };
}
Loading