codingoutloud
3/25/2018 - 7:11 PM

ReportBlockedByCSP

#r "Newtonsoft.Json"

using System.Net;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

#r "Newtonsoft.Json"

using System.Net;
using System.Net.Http;
using System.Text;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

public static async Task<string> GetUserAgentSummaryAsync(HttpRequest req, TraceWriter log)
{
    const bool verbose = false;

    var agents = req.Headers["User-Agent"];

    if (verbose) log.Info($"AGENTS COUNT = {agents.Count}");
    if (verbose) log.Info($"{agents}");

    var agentjson = $"{{ \"user_agent\": \"{agents[0]}\" }}";
    if (verbose) log.Info($"AGENT JSON = [{agentjson}]");

    const string useragent_apikey_header = "X-API-KEY";
    const string useragent_apikey_location = "HOSTHEADER_APIKEY";
    var useragent_apikey = System.Environment.GetEnvironmentVariable(useragent_apikey_location); // CONFIGURE IN YOUR ENVIRONMENT
    if (verbose) log.Info($"API KEY LENGTH = {useragent_apikey.Length}");

    const string useragent_apiurl = "https://api.whatismybrowser.com/api/v2/user_agent_parse";

    using (var httpClient = new HttpClient()) // okay for low-scale; else see https://docs.microsoft.com/en-us/azure/architecture/antipatterns/improper-instantiation/
    {
        var request = new HttpRequestMessage()
        {
            RequestUri = new Uri(useragent_apiurl),
            Method = HttpMethod.Post,
            Content = new StringContent(agentjson, Encoding.UTF8, "application/json")
        };
        request.Headers.Add(useragent_apikey_header, useragent_apikey);
        // not needed, not harmful: request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

        using (HttpResponseMessage response = await httpClient.SendAsync(request))
        {
            response.EnsureSuccessStatusCode(); // may throw

            var result = await response.Content.ReadAsStringAsync();

            if (verbose) log.Info($"RESULT = {result}");
            if (verbose) log.Info($"AGENTS = {agents}");
            
            var json = JObject.Parse(result);

            bool parsedOk = ((string) (json["result"]["code"])) == "success";
            bool is_abusive = (bool) (json["parse"]["is_abusive"]);
            if (is_abusive || ! parsedOk)
            {
                throw new Exception($"Could not parse user agent ({parsedOk}) or was malformed ({is_abusive})");
            }

            var browser = (string) json["parse"]["software_name"];
            var os = (string) json["parse"]["operating_system_name"];

            var text = $"{browser} on {os}";
            if (verbose) text = $"{text} [details: {result}]";
            return text;
        }
    }
}

const bool format = true;

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
    log.Info("Handling CSP Report.");
      
    //dynamic data = await req.Content.ReadAsAsync<object>();
    var json = req.Content.ReadAsStringAsync().Result;

    if (format) json = JValue.Parse(json).ToString(Formatting.Indented);

    log.Info($"CSP REPORT payload (JSON):\n{json}");

    var logicAppUrlBase = "https://prod-29.northcentralus.logic.azure.com:443";
    var logicAppUrlParams = "/workflows/90485f53082e492e8c4e52d3aa2843cb/triggers/manual/paths/invoke?api-version=2016-10-01&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=7CdQoZUrRsWgyGtCZUZcraj6A518oMwovAk9FuIjbLo";

    // ENRICH the usual CSP reporting JSON with User Agent, if it is available
    string summaryUserAgent;
    try
    {
        summaryUserAgent = await GetUserAgentSummaryAsync(req, log);
        JObject report = JObject.Parse(json);
        JObject channel = (JObject)report["csp-report"];
        channel.Property("blocked-uri").AddBeforeSelf(new JProperty("user-agent-summary", summaryUserAgent));
    }
    catch (Exception ex)
    {
        ; // continue
    }

    // TODO: figure out WHICH (whitelisted) Reporting endpoint posted the report
    // forward to Logic App using above credendials
    using (var client = new HttpClient())
    {
        client.BaseAddress = new Uri(logicAppUrlBase);
        var content = new StringContent(json, Encoding.UTF8, "application/json");
        var result = await client.PostAsync(logicAppUrlParams, content);
        string resultContent = await result.Content.ReadAsStringAsync();
        log.Info(resultContent);        
        if (result.IsSuccessStatusCode)
        {
            return req.CreateResponse(HttpStatusCode.OK);
        }
        else
        {
            return req.CreateResponse(HttpStatusCode.BadRequest, 
                "Failed while trying to forward to LA");
        }
    }
}