@Summary Upload task log snapshot @ID upload-task-log-snapshot @Security CoderSessionToken @Accept json @Tags Tasks @Param task path string true "Task ID" format(uuid) @Param format query string true "Snapshot format" enums(agentapi) @Param request body object true "Raw snapshot payload (structure d
(rw http.ResponseWriter, r *http.Request)
| 1112 | // @Success 204 |
| 1113 | // @Router /api/v2/workspaceagents/me/tasks/{task}/log-snapshot [post] |
| 1114 | func (api *API) postWorkspaceAgentTaskLogSnapshot(rw http.ResponseWriter, r *http.Request) { |
| 1115 | var ( |
| 1116 | ctx = r.Context() |
| 1117 | latestBuild = httpmw.LatestBuild(r) |
| 1118 | ) |
| 1119 | |
| 1120 | // Parse task ID from path. |
| 1121 | taskIDStr := chi.URLParam(r, "task") |
| 1122 | taskID, err := uuid.Parse(taskIDStr) |
| 1123 | if err != nil { |
| 1124 | httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ |
| 1125 | Message: "Invalid task ID format.", |
| 1126 | Detail: err.Error(), |
| 1127 | }) |
| 1128 | return |
| 1129 | } |
| 1130 | |
| 1131 | // Validate format parameter (required). |
| 1132 | p := httpapi.NewQueryParamParser().RequiredNotEmpty("format") |
| 1133 | format := p.String(r.URL.Query(), "", "format") |
| 1134 | p.ErrorExcessParams(r.URL.Query()) |
| 1135 | if len(p.Errors) > 0 { |
| 1136 | httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ |
| 1137 | Message: "Invalid query parameters.", |
| 1138 | Validations: p.Errors, |
| 1139 | }) |
| 1140 | return |
| 1141 | } |
| 1142 | if format != "agentapi" { |
| 1143 | httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ |
| 1144 | Message: "Invalid format parameter.", |
| 1145 | Detail: fmt.Sprintf(`Only "agentapi" format is currently supported, got %q.`, format), |
| 1146 | }) |
| 1147 | return |
| 1148 | } |
| 1149 | |
| 1150 | // Verify task exists before reading the potentially large payload. |
| 1151 | // This prevents DoS attacks where attackers spam large payloads for |
| 1152 | // non-existent or deleted tasks, forcing us to read 64KB into memory |
| 1153 | // and do expensive JSON operations before the database rejects it. |
| 1154 | // The UpsertTaskSnapshot will re-fetch for RBAC validation, but this |
| 1155 | // early check protects against malicious load. |
| 1156 | task, err := api.Database.GetTaskByID(ctx, taskID) |
| 1157 | if err != nil { |
| 1158 | if httpapi.Is404Error(err) { |
| 1159 | httpapi.ResourceNotFound(rw) |
| 1160 | return |
| 1161 | } |
| 1162 | httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ |
| 1163 | Message: "Internal error fetching task.", |
| 1164 | Detail: err.Error(), |
| 1165 | }) |
| 1166 | return |
| 1167 | } |
| 1168 | |
| 1169 | // Reject deleted tasks early. |
| 1170 | if task.DeletedAt.Valid { |
| 1171 | httpapi.ResourceNotFound(rw) |
nothing calls this directly
no test coverage detected