Skip to content

fix: add missing content-type headers in public api #385

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions public-api/v1alpha/lib/pipelines_api/logs/get.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ defmodule PipelinesAPI.Logs.Get do

use Plug.Builder

import PipelinesAPI.Util.APIResponse

require Logger
alias PipelinesAPI.Pipelines.Common, as: RespCommon
alias PipelinesAPI.Util.{Metrics}
Expand Down Expand Up @@ -49,7 +51,8 @@ defmodule PipelinesAPI.Logs.Get do
{:ok, token} ->
conn
|> put_resp_header("location", build_loghub2_url(conn, job_id, token))
|> send_resp(conn.status || 302, "")
|> put_status(conn.status || 302)
|> text("")

e ->
RespCommon.respond(e, conn)
Expand All @@ -73,7 +76,7 @@ defmodule PipelinesAPI.Logs.Get do
end

defp prepare_response(events) do
Enum.join(['{ "events": [', Enum.join(events, ","), "] }"], "")
Enum.join([~c'{ "events": [', Enum.join(events, ","), "] }"], "")
end

defp build_loghub2_url(conn, job_id, token) do
Expand Down
13 changes: 9 additions & 4 deletions public-api/v1alpha/lib/pipelines_api/pipelines/common.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ defmodule PipelinesAPI.Pipelines.Common do
@moduledoc false

import Plug.Conn
import PipelinesAPI.Util.APIResponse

def respond(state, conn, encode? \\ true)

Expand All @@ -28,13 +29,17 @@ defmodule PipelinesAPI.Pipelines.Common do
# Sobelow is always warning when using send_resp when value is not actual hardcoded string nor encoded,
# json encoded content is safe to use send_resp.
# sobelow_skip ["XSS.SendResp"]
defp respond_(conn, code, content, _encode? = true),
do: send_resp(conn, code, content |> Poison.encode!())
defp respond_(conn, code, content, _encode? = true) do
json(conn, content)
end

# The Sobelow.XSS.SendResp is focused on Phoenix apps so it does not recognise Plug.HTML.html_escape/1
# sobelow_skip ["XSS.SendResp"]
defp respond_(conn, code, content, _encode? = false),
do: send_resp(conn, code, Plug.HTML.html_escape(content))
defp respond_(conn, code, content, _encode? = false) do
conn
|> put_status(code)
|> text(Plug.HTML.html_escape(content))
end

def respond_paginated({:error, e}, conn), do: respond({:error, e}, conn)

Expand Down
19 changes: 14 additions & 5 deletions public-api/v1alpha/lib/pipelines_api/router.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
defmodule PipelinesAPI.Router do
use Plug.{Router, ErrorHandler}

import PipelinesAPI.Util.APIResponse

alias PipelinesAPI.Pipelines.Terminate
alias PipelinesAPI.Pipelines.DescribeTopology
alias PipelinesAPI.Pipelines.PartialRebuild
Expand Down Expand Up @@ -156,7 +158,7 @@ defmodule PipelinesAPI.Router do
match("/logs/:job_id", via: :get, to: PipelinesAPI.Logs.Get)

get "/health_check/ping" do
send_resp(conn, 200, "pong")
text(conn, "pong")
end

# sobelow_skip ["XSS.SendResp"]
Expand All @@ -167,20 +169,27 @@ defmodule PipelinesAPI.Router do
case reason.__struct__ do
Plug.Parsers.ParseError ->
%{exception: %{message: message}} = reason
send_resp(conn, conn.status, "Malformed request: " <> Plug.HTML.html_escape(message))

conn
|> put_status(conn.status)
|> text("Malformed request: " <> Plug.HTML.html_escape(message))

_ ->
send_resp(conn, conn.status, "Something went wrong")
conn
|> put_status(conn.status)
|> text("Something went wrong")
end
end

# Root path has to return 200 OK in order to pass health checks made by ingress
# on Kubernets
get "/" do
send_resp(conn, 200, "pong")
text(conn, "pong")
end

match _ do
send_resp(conn, 404, "oops")
conn
|> put_status(404)
|> text("oops")
end
end
46 changes: 46 additions & 0 deletions public-api/v1alpha/lib/pipelines_api/util/api_response.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
defmodule PipelinesAPI.Util.APIResponse do
@moduledoc false

@doc """
Sends JSON response.


iex> json(conn, %{id: 123})

"""
@spec json(Plug.Conn.t(), term) :: Plug.Conn.t()
def json(conn, data) do
content = Poison.encode!(data)
send_resp(conn, conn.status || 200, "application/json", content)
end

@doc """
Sends text response.

## Examples

iex> text(conn, "hello")

iex> text(conn, :implements_to_string)

"""
@spec text(Plug.Conn.t(), String.Chars.t()) :: Plug.Conn.t()
def text(conn, data) do
send_resp(conn, conn.status || 200, "text/plain", to_string(data))
end

defp send_resp(conn, default_status, default_content_type, body) do
conn
|> ensure_resp_content_type(default_content_type)
|> Plug.Conn.send_resp(conn.status || default_status, body)
end

defp ensure_resp_content_type(%Plug.Conn{resp_headers: resp_headers} = conn, content_type) do
if List.keyfind(resp_headers, "content-type", 0) do
conn
else
content_type = content_type <> "; charset=utf-8"
%Plug.Conn{conn | resp_headers: [{"content-type", content_type} | resp_headers]}
end
end
end