Webhook Comment Processing#
When a user comments on a pull request, Atlantis receives a provider-specific webhook, parses it into internal models, validates multiple guards, and then dispatches the command asynchronously. The full pipeline spans three layers:
- HTTP routing β
VCSEventsController.Post - Event parsing β provider-specific functions in
server/events/event_parser.go - Validation + execution β
handleCommentEventβRunCommentCommand
Provider-Specific Event Parsing#
Each VCS provider uses a distinct webhook payload. The EventParser in server/events/event_parser.go provides a dedicated function per provider to extract the raw comment body, the commenter identity, and the repository context into canonical internal models (models.Repo, models.User, models.PullRequest):
| Provider | Parse Function | Returns |
|---|---|---|
| GitHub | ParseGithubIssueCommentEvent | baseRepo, user, pullNum |
| GitLab | ParseGitlabMergeRequestCommentEvent | baseRepo, headRepo, commentID, user |
| Bitbucket Cloud | ParseBitbucketCloudPullCommentEvent | pull, baseRepo, headRepo, user, comment |
| Bitbucket Server | ParseBitbucketServerPullCommentEvent | pull, baseRepo, headRepo, user, comment |
| Gitea | ParseGiteaIssueCommentEvent | baseRepo, user, pullNum |
Key differences: GitHub and Gitea comment events only carry baseRepo + user + pullNum (the head repo is fetched lazily later), while Bitbucket variants include the full PullRequest model directly in the webhook payload. GitLab uniquely returns both baseRepo and headRepo from the event. Commit-level comments in GitLab are explicitly ignored .
HTTP routing to these handlers is header-driven: X-Github-Event β GitHub, X-Gitlab-Event β GitLab, X-Event-Key + X-Request-UUID β Bitbucket Cloud, X-Gitea-Event β Gitea .
Comment Parsing#
After the provider-specific parse, the raw comment text flows into CommentParser.Parse in server/events/comment_parser.go. This function:
- Trims & rejects multi-line comments (only a trailing double-newline from GitHub copy-paste is tolerated)
- Matches the executable name (
atlantis,run, or@<VCSUser>) β near-matches toterraformor the executable name produce a "Did you meanβ¦?" reply - Re-parses with
shlex.Splitto handle quoted arguments - Dispatches to per-command
pflag.FlagSetforplan,apply,unlock,version,approve_policies,import,state,cancel - Validates flags β blocked extra args (
-chdir,-plugin-dir) and path traversal in-d/--dirand workspace names are rejected
The result is a CommentParseResult containing either a parsed CommentCommand, an immediate CommentResponse (e.g., for help), or an Ignore signal.
Validation Guards in handleCommentEvent#
Once parsing succeeds, handleCommentEvent applies guards in this order before dispatching:
-
Ignore check β if
parseResult.Ignoreis true (non-command comment), returns 200 immediately with no side effects . -
Repo allowlist β
RepoAllowlistChecker.IsAllowlistedvalidates the repo's full name and VCS hostname against include/exclude wildcard rules. Failure returns HTTP 403 and (optionally) a PR comment; the error can be silenced viaSilenceAllowlistErrors. -
Emoji reaction β if configured, Atlantis reacts to the comment before proceeding .
-
Immediate response β
helpand invalid commands are replied to directly without enteringRunCommentCommand. -
Async dispatch β valid commands are handed off to
RunCommentCommandin a goroutine .
Validation Guards in RunCommentCommand#
DefaultCommandRunner.RunCommentCommand applies a second layer of guards after building the command.Context:
-
Repo metadata validation β
ensureValidRepoMetadatafetches or validates pull request data from the VCS API, normalizing the lazy-fetched fields for providers that didn't include them in the webhook. -
Fork policy β
validateCtxAndCommentrejects commands on fork PRs whenAllowForkPRs=false(comparing head repo owner vs. base repo owner), rejects commands on closed PRs (exceptunlock), and rejects PRs whose base branch doesn't match the configured branch filter. -
User permissions & var-file allowlist β
validateCommentCommandchecks team allowlist membership (hierarchical, up to 20 levels of descendant teams) and, forplancommands, validates that any-var-fileflags point only to allowlisted paths.
Key Files#
| File | Purpose |
|---|---|
server/controllers/events/events_controller.go | HTTP routing, handleCommentEvent (lines 673β751) |
server/events/event_parser.go | Provider-specific webhook β internal model parsing |
server/events/comment_parser.go | Raw comment β CommentCommand parsing and validation |
server/events/command_runner.go | RunCommentCommand with fork/PR-state/permission guards |
server/events/repo_allowlist_checker.go | Repo allowlist include/exclude matching |