<commit_before>using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Serilog.Context;
using System;
using System.Net;
using System.Net.Mime;
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Host.Components.Interop;
using Tgstation.Server.Host.Components.Interop.Bridge;

namespace Tgstation.Server.Host.Controllers
{
	/// <summary>
	/// <see cref="Controller"/> for recieving DMAPI requests from DreamDaemon.
	/// </summary>
	[Route("Bridge")]
	[Produces(MediaTypeNames.Application.Json)]
	public class BridgeController : Controller
	{
		/// <summary>
		/// Static counter for the number of requests processed.
		/// </summary>
		static long requestsProcessed;

		/// <summary>
		/// The <see cref="IBridgeDispatcher"/> for the <see cref="BridgeController"/>
		/// </summary>
		readonly IBridgeDispatcher bridgeDispatcher;

		/// <summary>
		/// The <see cref="ILogger"/> for the <see cref="BridgeController"/>
		/// </summary>
		readonly ILogger<BridgeController> logger;

		/// <summary>
		/// Initializes a new instance of the <see cref="BridgeController"/> <see langword="class"/>.
		/// </summary>
		/// <param name="bridgeDispatcher">The value of <see cref="bridgeDispatcher"/>.</param>
		/// <param name="applicationLifetime">The <see cref="IHostApplicationLifetime"/> of the server.</param>
		/// <param name="logger">The value of <see cref="logger"/>.</param>
		public BridgeController(IBridgeDispatcher bridgeDispatcher, IHostApplicationLifetime applicationLifetime, ILogger<BridgeController> logger)
		{
			this.bridgeDispatcher = bridgeDispatcher ?? throw new ArgumentNullException(nameof(bridgeDispatcher));
			if (applicationLifetime == null)
				throw new ArgumentNullException(nameof(applicationLifetime));

			this.logger = logger ?? throw new ArgumentNullException(nameof(logger));

			applicationLifetime.ApplicationStopped.Register(() => requestsProcessed = 0);
		}

		/// <summary>
		/// Processes a bridge request.
		/// </summary>
		/// <param name="data">JSON encoded <see cref="BridgeParameters"/>.</param>
		/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
		/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> for the operation.</returns>
		[HttpGet]
		public async Task<IActionResult> Process([FromQuery]string data, CancellationToken cancellationToken)
		{
			// Nothing to see here
			var remoteIP = Request.HttpContext.Connection.RemoteIpAddress;
			if (!IPAddress.IsLoopback(remoteIP))
			{
				logger.LogTrace("Ignoring remote bridge request from {0}", remoteIP);
				return NotFound();
			}

			using (LogContext.PushProperty("Bridge", Interlocked.Increment(ref requestsProcessed)))
			{
				BridgeParameters request;
				try
				{
					request = JsonConvert.DeserializeObject<BridgeParameters>(data, DMApiConstants.SerializerSettings);
				}
				catch (Exception ex)
				{
					logger.LogWarning(ex, "Error deserializing bridge request: {0}", data);
					return BadRequest();
				}

				logger.LogTrace("Bridge Request: {0}", data);

				var response = await bridgeDispatcher.ProcessBridgeRequest(request, cancellationToken).ConfigureAwait(false);
				if (response == null)
					Forbid();

				var responseJson = JsonConvert.SerializeObject(response, DMApiConstants.SerializerSettings);
				logger.LogTrace("Bridge Response: {0}", responseJson);
				return Content(responseJson, MediaTypeNames.Application.Json);
			}
		}
	}
}
<commit_msg>403 instead of 404 on foreign bridge request<commit_after>using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Serilog.Context;
using System;
using System.Net;
using System.Net.Mime;
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Host.Components.Interop;
using Tgstation.Server.Host.Components.Interop.Bridge;

namespace Tgstation.Server.Host.Controllers
{
	/// <summary>
	/// <see cref="Controller"/> for recieving DMAPI requests from DreamDaemon.
	/// </summary>
	[Route("Bridge")]
	[Produces(MediaTypeNames.Application.Json)]
	public class BridgeController : Controller
	{
		/// <summary>
		/// Static counter for the number of requests processed.
		/// </summary>
		static long requestsProcessed;

		/// <summary>
		/// The <see cref="IBridgeDispatcher"/> for the <see cref="BridgeController"/>
		/// </summary>
		readonly IBridgeDispatcher bridgeDispatcher;

		/// <summary>
		/// The <see cref="ILogger"/> for the <see cref="BridgeController"/>
		/// </summary>
		readonly ILogger<BridgeController> logger;

		/// <summary>
		/// Initializes a new instance of the <see cref="BridgeController"/> <see langword="class"/>.
		/// </summary>
		/// <param name="bridgeDispatcher">The value of <see cref="bridgeDispatcher"/>.</param>
		/// <param name="applicationLifetime">The <see cref="IHostApplicationLifetime"/> of the server.</param>
		/// <param name="logger">The value of <see cref="logger"/>.</param>
		public BridgeController(IBridgeDispatcher bridgeDispatcher, IHostApplicationLifetime applicationLifetime, ILogger<BridgeController> logger)
		{
			this.bridgeDispatcher = bridgeDispatcher ?? throw new ArgumentNullException(nameof(bridgeDispatcher));
			if (applicationLifetime == null)
				throw new ArgumentNullException(nameof(applicationLifetime));

			this.logger = logger ?? throw new ArgumentNullException(nameof(logger));

			applicationLifetime.ApplicationStopped.Register(() => requestsProcessed = 0);
		}

		/// <summary>
		/// Processes a bridge request.
		/// </summary>
		/// <param name="data">JSON encoded <see cref="BridgeParameters"/>.</param>
		/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
		/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> for the operation.</returns>
		[HttpGet]
		public async Task<IActionResult> Process([FromQuery]string data, CancellationToken cancellationToken)
		{
			// Nothing to see here
			var remoteIP = Request.HttpContext.Connection.RemoteIpAddress;
			if (!IPAddress.IsLoopback(remoteIP))
			{
				logger.LogTrace("Rejecting remote bridge request from {0}", remoteIP);
				return Forbid();
			}

			using (LogContext.PushProperty("Bridge", Interlocked.Increment(ref requestsProcessed)))
			{
				BridgeParameters request;
				try
				{
					request = JsonConvert.DeserializeObject<BridgeParameters>(data, DMApiConstants.SerializerSettings);
				}
				catch (Exception ex)
				{
					logger.LogWarning(ex, "Error deserializing bridge request: {0}", data);
					return BadRequest();
				}

				logger.LogTrace("Bridge Request: {0}", data);

				var response = await bridgeDispatcher.ProcessBridgeRequest(request, cancellationToken).ConfigureAwait(false);
				if (response == null)
					Forbid();

				var responseJson = JsonConvert.SerializeObject(response, DMApiConstants.SerializerSettings);
				logger.LogTrace("Bridge Response: {0}", responseJson);
				return Content(responseJson, MediaTypeNames.Application.Json);
			}
		}
	}
}

{'fraction_non_alphanumeric': 0.1340407707344317, 'fraction_numerical': 0.002234012845573862, 'mean_word_length': 4.46793893129771, 'pattern_counts': {'":': 0, '<': 87, '<?xml version=': 0, '>': 89, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4438643', 'n_tokens_mistral': 2260, 'n_tokens_neox': 1962, 'n_words': 481}