Bringing Semgrep to Slack’s Hack Codebase

Slack runs more than 5 million lines of Hack, a language derived from Facebook’s work on a typed PHP. Because Hack is a separate language with no broadly available static analysis tools, the company’s Product Security team faced a gap in its ability to automatically scan for vulnerabilities. Over the course of a summer, two software engineering interns, Nicholas Lin and David Frankel, tackled this problem by extending the open-source tool Semgrep—already in use at Slack for six other languages—to understand and analyze Hack code.

The grammar problem

Building a new static analysis tool from scratch would have been prohibitively complex. Instead, the team focused on two questions: what are the grammar rules for Hack, and how can Semgrep interpret them? Like human languages, programming languages have a formal grammar that defines their structure. Applying that grammar to source code produces a concrete syntax tree (CST)—an exact structural representation of the parsed code.

Slack engineer Antonio Ochoa Solano had already been developing a Hack grammar for Tree-sitter, a library that generates parsers from grammar rules. The interns built upon that foundation, testing the grammar against internal repositories and prioritizing fixes based on error counts. The key metric was parse rate: the proportion of code that could be correctly converted into a CST. By reducing unparsable lines from more than 120,000 to just 15, the team achieved a parse rate greater than 99.999% across the entire codebase.

The grammar is now open source on GitHub, with ongoing work to reach a 100% parse rate.

Mapping Hack to Semgrep’s AST

Semgrep cannot consume a Tree-sitter CST directly. It relies on an abstract syntax tree (AST), which strips out unnecessary syntax like comments and parentheses and provides a common structure across all supported languages. This loose coupling is what makes Semgrep extensible.

To bridge the gap, the interns wrote a custom parser in OCaml that maps each Hack construct from the CST to Semgrep’s AST. The process was incremental: identify a language construct, observe its representation in the grammar and CST, determine the proper AST mapping, and implement it. This was the most intensive part of the project, drawing on knowledge of Hack, Tree-sitter, Semgrep, and OCaml. The parser currently achieves a 99.9% parse rate, with further improvements planned.

Pattern matching and rules

The final step was enabling Semgrep’s pattern syntax to work with Hack. Semgrep rules use a special syntax not parsable in Hack itself—for instance, to flag calls to the insecure MD5 hashing algorithm, a rule pattern might look like md5(…), where the ellipsis matches any arguments. The team extended the Tree-sitter parser to track this Semgrep-specific syntax, allowing for complex rules that check nested calls and statements.

These rules support Slack’s efforts to safely handle user input, complete authentication checks, and run OWASP Top Ten vulnerability detection across all Hack code. The implementation runs on new code before deployment and daily across the entire codebase.

The project follows a clear pipeline: raw Hack source is converted to a CST, generalized to an AST, then analyzed by Semgrep against security rules to identify potential vulnerabilities. With this infrastructure in place, Slack can proactively secure its largest codebase and prevent the introduction of new security issues.