How Cal.com Rebuilt AppSec After Going Closed Source
How Cal.com consolidated noisy security tooling into one continuous, context-aware pull request security program with Gecko.
Gecko Security

Everyone uses static code analysis tools to find vulnerabilities before code ships. The concept is straightforward: analyze source code without executing it, catch problems like SQL injection and XSS through pattern matching, fix issues during code review instead of after production breaches. Where this breaks down is business logic. Your SAST tool scans every code path and flags hardcoded secrets perfectly, but it can’t tell you whether the authorization check three functions deep is actually validating the right permissions for your use case.
TLDR:
Static code analysis analyzes source code without executing it. You write code, commit it to your repository, and the analysis happens before deployment or testing. The process catches security vulnerabilities, code quality issues, and violations of secure coding practices early when they’re cheaper to fix.
The tools parse your source code into a structured format, apply rules to identify potential problems like SQL injection risks or buffer overflows, then report findings to developers. No running application required.
Static analysis tools convert your code into analyzable structures through several technical steps.
Lexical analysis breaks source code into tokens: keywords, operators, identifiers, and literals. These tokens feed into a parser that builds an Abstract Syntax Tree (AST), where each node represents a construct in your source code.
Tools then construct a control flow graph mapping every possible execution path, showing how program control moves between statements, branches, loops, and function calls.
Data flow analysis tracks how information moves through these paths, identifying where variables get defined, used, and modified. Taint analysis follows untrusted input to check if it reaches sensitive operations without sanitization.
Pattern matching applies rules against these representations, searching for known vulnerability patterns and flagging potential issues.
Runtime analysis tests your running application, executing code with real inputs and monitoring behavior at runtime. Penetration testing, fuzzing, and security scanners that probe live endpoints fall into this category.
The key difference: static analysis reviews source code before execution, while runtime analysis tests running applications. Static tools scan every code path including rarely executed branches. Runtime tools only test paths they can reach during scanning.
Static analysis finds potential vulnerabilities early in development. Runtime analysis confirms which issues are exploitable in production environments and catches configuration problems static tools miss.
Pattern-based analysis matches code against known anti-patterns and vulnerability signatures. You get alerts for things like hardcoded credentials or insecure cryptographic functions.
Flow-based analysis traces data and control flow across your codebase. This includes taint analysis and control flow techniques, tracking whether untrusted input reaches dangerous operations.
Security-focused analysis (SAST) targets exploitable weaknesses like injection flaws, authentication bypasses, and insecure configurations.
Complexity analysis measures code maintainability through metrics like cyclomatic complexity and nesting depth.
Style analysis enforces coding standards and best practices without looking for vulnerabilities.
Static analysis catches vulnerabilities before code reaches production, when fixes cost 10 to 100 times less than post-deployment patches. You spot security flaws during code review instead of after a breach.
Automated checks accelerate development cycles by flagging issues instantly at commit time. Your developers get immediate feedback without waiting for security team reviews or penetration tests.
Compliance frameworks like PCI-DSS and NIST require secure code practices. Static analysis provides audit trails showing you scan code regularly and fix findings, satisfying regulatory requirements.
False positives remain the biggest practical hurdle. Untuned SAST tools produce 30-60% false positives, though proper configuration can reduce that to 10-20%. Research shows 45% of organizations struggle with high false-positive rates, averaging 6 false positives per 1,000 lines of code.
False negatives create blind spots. Pattern-based tools miss vulnerabilities that don’t match known signatures, particularly business logic flaws where the code executes correctly but does the wrong thing.
Dynamically typed languages make this worse. Python and JavaScript let objects change type at runtime, so the static snapshot of your code doesn’t reflect what actually executes. Most SAST tools handle this with AST parsing, which captures the syntax tree of individual files but can’t resolve complex call chains. When a function’s return type depends on runtime conditions, the parser loses the thread entirely. You end up with missed vulnerabilities at exactly the points where untrusted data crosses function or module boundaries, the places that matter most.
Runtime context disappears during static analysis. Tools can’t see database configurations, environment variables, or how components interact at runtime.
Static analysis catches syntactic vulnerabilities rooted in code structure. SQL injection occurs when user input concatenates into queries, cross-site scripting when output lacks encoding, and buffer overflows from unsafe memory operations.
Pattern matching identifies eval() calls with user input, insecure deserialization, weak cryptographic algorithms like MD5, and hardcoded credentials. Configuration mistakes like missing HTTPS enforcement or disabled certificate validation surface easily.
Semantic vulnerabilities resist traditional SAST because code executes correctly but behaves incorrectly. Missing authorization checks, privilege escalation bugs, and insecure direct object references require understanding intent beyond syntax alone.
| Vulnerability Type | Pattern-Based SAST Detection | Semantic Analysis Detection | Why the Difference Matters |
|---|---|---|---|
| SQL Injection | High accuracy through pattern matching of concatenated queries and unsafe database calls | Equivalent detection with additional context about data flow across service boundaries | Syntactic vulnerability with well-defined anti-patterns that traditional tools handle effectively |
| Cross-Site Scripting (XSS) | Reliable detection of unescaped output and missing sanitization in templates | Enhanced detection tracking taint propagation through framework layers | Clear pattern signatures make this a solved problem for modern SAST tools |
| Hardcoded Credentials | Excellent detection through regex patterns matching password strings and API keys | Same detection plus identification of credential usage in authorization logic | Pure pattern matching works because credentials follow predictable formats |
| Broken Access Control | Cannot determine if authorization logic is correct or sufficient for business requirements | Traces execution paths to identify missing authorization checks and privilege escalation vectors | Affects 100% of tested applications because correctness depends on application-specific intent, not syntax |
| Insecure Direct Object References (IDOR) | Misses most cases because tools cannot determine if access validation matches ownership requirements | Maps data flows from user input to database queries, identifying missing ownership checks | Requires understanding whether the code validates the relationship between user and resource |
| Privilege Escalation | Flags obvious role comparisons but cannot reason about multi-step permission chains | Analyzes call graphs to find paths where higher-level permissions persist beyond intended scope | Business logic flaw requiring semantic understanding of permission propagation across functions |
| AI-Generated Authorization Gaps | Detects only if generated code matches known bad patterns, missing novel logic errors | Identifies missing conditional checks and validates authorization exists at trust boundaries | AI produces syntactically correct code with incorrect security assumptions that pattern matching cannot catch |
OWASP’s 2025 Top 10 reveals a striking pattern: broken access control affects 100% of applications tested, maintaining its position as the #1 vulnerability. Meanwhile, injection attacks dropped from first place in 2017 to fifth in 2025.
Injection got solved because frameworks made secure practices the default. Parameterized queries, prepared statements, and ORM protections became standard. Developers learned to separate code from data, and static analysis tools got good at spotting violations.
Authorization bugs tell a different story. Pattern matching can’t tell you if an authorization check is correct because “correct” depends on your application’s specific requirements.
AI code assistants accelerate development speed while introducing security gaps. Only 55% of AI-generated code was secure, with nearly half containing known flaws.
The issue extends beyond simple bugs. Engineers using AI write less secure code while trusting the output more, creating blind spots where vulnerabilities pass through review undetected.
AI models optimize for functionality over security, producing syntactically correct code that compiles but lacks proper authorization checks, conditional logic validation, or access control patterns. When developers generate code faster than manual auditing allows, static analysis becomes the first line of defense against AI-introduced vulnerabilities reaching production systems.
Language support matters first. Your tool must parse your stack’s languages accurately. Python shops need different parsers than Java teams, and polyglot architectures require multi-language coverage that preserves semantic relationships across service boundaries.
Integration capabilities determine adoption success. Git hooks, CI/CD pipeline plugins, and IDE extensions let developers see findings where they work. API access pipes results into ticketing systems and dashboards.
False positive management separates usable tools from noise generators. Look for customizable rule sets, suppression mechanisms, and baseline capabilities that flag only new issues in existing codebases.
Reporting needs vary by audience. Developers want file locations and fix guidance. Security teams need risk scoring and trend analysis. Compliance officers require audit trails showing coverage and remediation rates.
CI/CD integration catches issues at commit time, before code reaches main branches. Run scans on pull requests and block merges that introduce high-severity findings. Gate deployments on passing security checks.
Start with baselines for existing codebases to prevent teams from drowning in legacy issues they can’t immediately fix. Scan current code, accept the state, then track only new findings moving forward.
Customize rulesets to your architecture and risk tolerance. Disable rules generating noise in your environment and add custom patterns for organization-specific security requirements.
Combine static analysis with other methods, since SAST finds code-level flaws while DAST tests running applications and manual reviews catch business logic gaps that tools miss.
Pattern-based SAST evolved to catch syntactic vulnerabilities but cannot reason about business logic. The next generation uses semantic understanding instead of pattern matching.
Semantic approaches build code property graphs that preserve how functions connect across files and services. This goes beyond AST parsing by maintaining type information, call chains, and relationships between components.
Tools using semantic indexing can answer questions traditional SAST cannot: Does this execution path include authorization? Should this parameter be validated but isn’t? Where does user context get dropped between layers?
AI reasoning on accurate semantic models detects missing checks and incorrect conditional logic by understanding intent instead of syntax alone.
We built Gecko to find the business logic vulnerabilities traditional SAST misses. Instead of pattern matching, we use compiler-accurate indexing built on language servers (the same technology that powers IDE autocompletion), not AST parsing. Gecko resolves types, call chains, and dependencies with precision even in dynamically typed codebases like Python and JavaScript, where objects change type at runtime and AST-based tools lose the thread entirely. That foundation unlocks capabilities AST parsing can’t reach: scanning across microservices, detecting vulnerabilities that span trust boundaries between services, and resolving cross-repo dependencies in multi-repo architectures so a flaw that originates in one service and surfaces in another doesn’t slip through undetected.
This catches authorization bypasses, privilege escalation paths, and IDOR vulnerabilities by reasoning about whether security logic is actually correct. We’ve found 30+ open source CVEs using this approach, finding flaws that previously only surfaced during manual penetration testing.
The result: roughly 20% false positive rate while catching business logic flaws that matter.
Static analysis finds vulnerabilities in code you write, runtime analysis tests what actually runs, and neither is sufficient on its own. Static code analysis software works best when it goes beyond pattern matching to understand how your application logic connects across files and services. You’ll get better results by running both types of scanning at different pipeline stages. Security tools should fit your workflow, not force you to change how you ship code.

Artemiy Malyshau
Co-founder & CTO
Artemiy served in an elite unit of the Austrian Cyber Forces, defending national infrastructure He was then the first employee at a government-backed cybersecurity research group, where he led security projects for Interpol and national governments. At Gecko he builds the platform trusted to sit inside Fortune 500 codebases, and holds it to the standard those governments taught him.
The latest news, technologies, and resources from our team.
How Cal.com consolidated noisy security tooling into one continuous, context-aware pull request security program with Gecko.
Gecko Security
Authorization bypass in n8n’s dynamic-credentials OAuth endpoints allows any authenticated user to operate on another user’s OAuth credential by supplying its ID, enabling unauthorized OAuth rebinding and revocation.
Artemiy Malyshau
An IDOR vulnerability in n8n’s public variables API allows authenticated users to read project variables outside their authorized scope, exposing secrets across project boundaries.
Artemiy Malyshau
Learn API scanning for automated security testing. Find vulnerabilities from broken authentication to business logic flaws in your endpoints.
Artemiy Malyshau
A complete guide to automated pentest tools and best practices. Learn what works, what doesn’t, and how to implement continuous security testing.
Artemiy Malyshau
Compare the best AI-powered application security testing tools. Find which tools detect business logic flaws and broken access control.
Artemiy Malyshau
Occasional updates, new content, and insights. No spam; unsubscribe anytime.