Skip to content

Commit b6a422a

Browse files
committed
Enforce explicitly setting SHA in API requests
Signed-off-by: Lukas Peter Aldershaab <[email protected]>
1 parent de1d8dc commit b6a422a

File tree

3 files changed

+79
-61
lines changed

3 files changed

+79
-61
lines changed

runatlantis.io/docs/api-endpoints.md

+66-60
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ Aside from interacting via pull request comments, Atlantis could respond to a li
44

55
## Main Endpoints
66

7-
The API endpoints in this section are disabled by default, since these API endpoints could change the infrastructure directly.
7+
The API endpoints in this section are disabled by default, since these API endpoints could change the infrastructure
8+
directly.
89
To enable the API endpoints, `api-secret` should be configured.
910

1011
:::tip Prerequisites
@@ -21,13 +22,14 @@ Execute [atlantis plan](using-atlantis.md#atlantis-plan) on the specified reposi
2122

2223
#### Parameters
2324

24-
| Name | Type | Required | Description |
25-
|------------|---------|----------|------------------------------------------|
26-
| Repository | string | Yes | Name of the Terraform repository |
27-
| Ref | string | Yes | Git reference, like a branch name |
28-
| Type | string | Yes | Type of the VCS provider (Github/Gitlab) |
29-
| Paths | Path | Yes | Paths to the projects to run the plan |
30-
| PR | int | No | Pull Request number |
25+
| Name | Type | Required | Description |
26+
|------------|--------|----------|--------------------------------------------------------------------------------------|
27+
| Repository | string | Yes | Name of the Terraform repository |
28+
| Ref | string | Yes | Git reference, like a branch name |
29+
| Type | string | Yes | Type of the VCS provider (Github/Gitlab) |
30+
| Paths | Path | Yes | Paths to the projects to run the plan |
31+
| Sha | string | No | SHA of the specific commit to checkout. This is not required but heavily encouraged. |
32+
| PR | int | No | Pull Request number |
3133

3234
#### Path
3335

@@ -49,6 +51,7 @@ curl --request POST 'https://<ATLANTIS_HOST_NAME>/api/plan' \
4951
--data-raw '{
5052
"Repository": "repo-name",
5153
"Ref": "main",
54+
"Sha": "940222c757012e0922c5fc1e03d5574c5ce79994",
5255
"Type": "Github",
5356
"Paths": [{
5457
"Directory": ".",
@@ -62,29 +65,29 @@ curl --request POST 'https://<ATLANTIS_HOST_NAME>/api/plan' \
6265

6366
```json
6467
{
65-
"Error": null,
66-
"Failure": "",
67-
"ProjectResults": [
68-
{
69-
"Command": 1,
70-
"RepoRelDir": ".",
71-
"Workspace": "default",
72-
"Error": null,
73-
"Failure": "",
74-
"PlanSuccess": {
75-
"TerraformOutput": "<redacted>",
76-
"LockURL": "<redacted>",
77-
"RePlanCmd": "atlantis plan -d .",
78-
"ApplyCmd": "atlantis apply -d .",
79-
"HasDiverged": false
80-
},
81-
"PolicyCheckSuccess": null,
82-
"ApplySuccess": "",
83-
"VersionSuccess": "",
84-
"ProjectName": ""
85-
}
86-
],
87-
"PlansDeleted": false
68+
"Error": null,
69+
"Failure": "",
70+
"ProjectResults": [
71+
{
72+
"Command": 1,
73+
"RepoRelDir": ".",
74+
"Workspace": "default",
75+
"Error": null,
76+
"Failure": "",
77+
"PlanSuccess": {
78+
"TerraformOutput": "<redacted>",
79+
"LockURL": "<redacted>",
80+
"RePlanCmd": "atlantis plan -d .",
81+
"ApplyCmd": "atlantis apply -d .",
82+
"HasDiverged": false
83+
},
84+
"PolicyCheckSuccess": null,
85+
"ApplySuccess": "",
86+
"VersionSuccess": "",
87+
"ProjectName": ""
88+
}
89+
],
90+
"PlansDeleted": false
8891
}
8992
```
9093

@@ -96,13 +99,14 @@ Execute [atlantis apply](using-atlantis.md#atlantis-apply) on the specified repo
9699

97100
#### Parameters
98101

99-
| Name | Type | Required | Description |
100-
|------------|--------|----------|------------------------------------------|
101-
| Repository | string | Yes | Name of the Terraform repository |
102-
| Ref | string | Yes | Git reference, like a branch name |
103-
| Type | string | Yes | Type of the VCS provider (Github/Gitlab) |
104-
| Paths | Path | Yes | Paths to the projects to run the apply |
105-
| PR | int | No | Pull Request number |
102+
| Name | Type | Required | Description |
103+
|------------|--------|----------|--------------------------------------------------------------------------------------|
104+
| Repository | string | Yes | Name of the Terraform repository |
105+
| Ref | string | Yes | Git reference, like a branch name |
106+
| Type | string | Yes | Type of the VCS provider (Github/Gitlab) |
107+
| Paths | Path | Yes | Paths to the projects to run the apply |
108+
| Sha | string | No | SHA of the specific commit to checkout. This is not required but heavily encouraged. |
109+
| PR | int | No | Pull Request number |
106110

107111
#### Path
108112

@@ -124,6 +128,7 @@ curl --request POST 'https://<ATLANTIS_HOST_NAME>/api/apply' \
124128
--data-raw '{
125129
"Repository": "repo-name",
126130
"Ref": "main",
131+
"Sha": "940222c757012e0922c5fc1e03d5574c5ce79994",
127132
"Type": "Github",
128133
"Paths": [{
129134
"Directory": ".",
@@ -137,29 +142,30 @@ curl --request POST 'https://<ATLANTIS_HOST_NAME>/api/apply' \
137142

138143
```json
139144
{
140-
"Error": null,
141-
"Failure": "",
142-
"ProjectResults": [
143-
{
144-
"Command": 0,
145-
"RepoRelDir": ".",
146-
"Workspace": "default",
147-
"Error": null,
148-
"Failure": "",
149-
"PlanSuccess": null,
150-
"PolicyCheckSuccess": null,
151-
"ApplySuccess": "<redacted>",
152-
"VersionSuccess": "",
153-
"ProjectName": ""
154-
}
155-
],
156-
"PlansDeleted": false
145+
"Error": null,
146+
"Failure": "",
147+
"ProjectResults": [
148+
{
149+
"Command": 0,
150+
"RepoRelDir": ".",
151+
"Workspace": "default",
152+
"Error": null,
153+
"Failure": "",
154+
"PlanSuccess": null,
155+
"PolicyCheckSuccess": null,
156+
"ApplySuccess": "<redacted>",
157+
"VersionSuccess": "",
158+
"ProjectName": ""
159+
}
160+
],
161+
"PlansDeleted": false
157162
}
158163
```
159164

160165
## Other Endpoints
161166

162-
The endpoints listed in this section are non-destructive and therefore don't require authentication nor special secret token.
167+
The endpoints listed in this section are non-destructive and therefore don't require authentication nor special secret
168+
token.
163169

164170
### GET /status
165171

@@ -177,9 +183,9 @@ curl --request GET 'https://<ATLANTIS_HOST_NAME>/status'
177183

178184
```json
179185
{
180-
"shutting_down": false,
181-
"in_progress_operations": 0,
182-
"version": "0.22.3"
186+
"shutting_down": false,
187+
"in_progress_operations": 0,
188+
"version": "0.22.3"
183189
}
184190
```
185191

@@ -199,6 +205,6 @@ curl --request GET 'https://<ATLANTIS_HOST_NAME>/healthz'
199205

200206
```json
201207
{
202-
"status": "ok"
208+
"status": "ok"
203209
}
204210
```

server/controllers/api_controller.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ type APIRequest struct {
3939
Repository string `validate:"required"`
4040
Ref string `validate:"required"`
4141
Type string `validate:"required"`
42+
Sha string
4243
PR int
4344
Projects []string
4445
Paths []struct {
@@ -233,13 +234,22 @@ func (a *APIController) apiParseAndValidate(r *http.Request) (*APIRequest, *comm
233234
return nil, nil, http.StatusForbidden, fmt.Errorf("repo not allowlisted")
234235
}
235236

237+
commit := request.Sha
238+
if commit == "" {
239+
// DEPRECATED: To maintain legacy behaviour, we set the commit to the ref. However,
240+
// using the ref does not work in many cases and can also yield unexpected results
241+
// as a ref is a moving target while a SHA is a static target.
242+
commit = request.Ref
243+
a.Logger.Warn("API was called with an empty SHA, this is deprecated. When calling the Atlantis API, the SHA should be specified explicitly.")
244+
}
245+
236246
return &request, &command.Context{
237247
HeadRepo: baseRepo,
238248
Pull: models.PullRequest{
239249
Num: request.PR,
240250
BaseBranch: request.Ref,
241251
HeadBranch: request.Ref,
242-
HeadCommit: request.Ref,
252+
HeadCommit: commit,
243253
BaseRepo: baseRepo,
244254
},
245255
Scope: a.Scope,

server/controllers/api_controller_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ func TestAPIController_Plan(t *testing.T) {
2828
body, _ := json.Marshal(controllers.APIRequest{
2929
Repository: "Repo",
3030
Ref: "main",
31+
Sha: "abc123",
3132
Type: "Gitlab",
3233
Projects: []string{"default"},
3334
})
@@ -45,6 +46,7 @@ func TestAPIController_Apply(t *testing.T) {
4546
body, _ := json.Marshal(controllers.APIRequest{
4647
Repository: "Repo",
4748
Ref: "main",
49+
Sha: "abc123",
4850
Type: "Gitlab",
4951
Projects: []string{"default"},
5052
})

0 commit comments

Comments
 (0)