Skip to content
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

Add IExceptionHandler and use it to warn about old localtest #998

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

ivarne
Copy link
Member

@ivarne ivarne commented Dec 19, 2024

This PR introduces a new concept IExceptionHandler that currently only handles the new AltinnAppException and customises the ProblemDetails response to include the data from the exception

The use case is to warn about old localtest, but in the future I think this Is a richer way to both ensure that exceptions gets logged, and that api users gets more helpful error messages.

New stuff

  • New RequestExceptionHandler: IExceptionHandler to create ProblemDetails
  • New AltinnAppException with the fields of ProblemDetails
  • Throw exception for old localtest with text for ProblemDetails about upgrading
  • Modify catch blocks to let the exception bubble through.

Verification

  • Your code builds clean without any errors or warnings
  • Manual testing done (required)
  • Relevant automated test added (if you find this hard, leave it and we'll help out)
  • All tests run green

Documentation

  • User documentation is updated with a separate linked PR in altinn-studio-docs. (if applicable)

* New RequestExceptionHandler: IExceptionHandler to create ProblemDetails
* New AltinnAppException with the fields of ProblemDetails
* Throw exception for old localtest with text for ProblemDetails about upgrading
* Modify callstack to let the exception bubble through.
@ivarne ivarne added the feature Label Pull requests with new features. Used when generation releasenotes label Dec 19, 2024
@ivarne ivarne requested a review from martinothamar December 19, 2024 22:00
internal class RequestExceptionHandler : IExceptionHandler
{
private readonly IProblemDetailsService _problemDetailsService;
private ILogger<RequestExceptionHandler> _logger;

Check notice

Code scanning / CodeQL

Missed 'readonly' opportunity Note

Field '_logger' can be 'readonly'.
}

_logger.LogError($"Unable to update instance process with instance id {instance.Id}");

Check failure

Code scanning / CodeQL

Log entries created from user input High

This log entry depends on a
user-provided value
.
This log entry depends on a
user-provided value
.
This log entry depends on a
user-provided value
.
This log entry depends on a
user-provided value
.
This log entry depends on a
user-provided value
.
This log entry depends on a
user-provided value
.
This log entry depends on a
user-provided value
.
This log entry depends on a
user-provided value
.
This log entry depends on a
user-provided value
.
This log entry depends on a
user-provided value
.
This log entry depends on a
user-provided value
.
This log entry depends on a
user-provided value
.

Copilot Autofix AI about 1 month ago

To fix the problem, we need to sanitize the instance.Id before logging it. This can be done by removing any newline characters from the instance.Id to prevent log forging. We will use the String.Replace method to achieve this.

  1. Identify the line where the instance.Id is logged.
  2. Sanitize the instance.Id by removing newline characters before logging it.
  3. Ensure that the fix does not change the existing functionality of the code.
Suggested changeset 1
src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs b/src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs
--- a/src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs
+++ b/src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs
@@ -206,3 +206,3 @@
 
-        _logger.LogError($"Unable to update instance process with instance id {instance.Id}");
+        _logger.LogError($"Unable to update instance process with instance id {instance.Id.Replace(Environment.NewLine, "").Replace("\n", "").Replace("\r", "")}");
         throw await PlatformHttpException.CreateAsync(response);
EOF
@@ -206,3 +206,3 @@

_logger.LogError($"Unable to update instance process with instance id {instance.Id}");
_logger.LogError($"Unable to update instance process with instance id {instance.Id.Replace(Environment.NewLine, "").Replace("\n", "").Replace("\r", "")}");
throw await PlatformHttpException.CreateAsync(response);
Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
public bool IsForbidden
{
// ReSharper disable once ValueParameterNotUsed
init { StatusCode = StatusCodes.Status403Forbidden; }

Check warning

Code scanning / CodeQL

Property value is not used when setting a property Warning

Value ignored when setting property.
public bool IsBadRequest
{
// ReSharper disable once ValueParameterNotUsed
init { StatusCode = StatusCodes.Status400BadRequest; }

Check warning

Code scanning / CodeQL

Property value is not used when setting a property Warning

Value ignored when setting property.
@@ -369,6 +370,12 @@ await _instanceClient.DeleteInstance(
_logger.LogInformation("Events sent to process engine: {Events}", change?.Events);
await _processEngine.HandleEventsAndUpdateStorage(instance, null, change?.Events);
}
// Let the AltinnAppException through as it is handled in IExceptionHandler
catch (AltinnAppException)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The part I don't like about this is the kind of coupling we get that isn't expressed anywhere (at least not cohesively). IMO it is hard to trace and maintain over time, so I think it's not a good solution for something to be used application-wide. In this particular case, the caller and callee have to be kept in sync, but the compiler doesn't help us :(

Since this is all internal though, we could let this through as long as we think of it as a temporary solution to this specific problem.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you object to this specific case of letting an exception through (which I totally agree, but wanted to preserve behavior). Or the general concept of throwing exceptions with explicit problem details info and a central catch middleware?

If this approach is accepted it would obviously cause a bigger change to how we handle errors.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have also purposefully limited the available status codes that can be returned, to reduce surprises for api documentation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The latter, exception based error handling, using IExceptionHandler in this case. IExceptionHandler is one of those global mechanisms that might create surprises for app devs in the same way we encountered with JsonOptions. So in that sense I like local try/catch better

@@ -16,10 +16,7 @@ public static class WebApplicationBuilderExtensions
public static IApplicationBuilder UseAltinnAppCommonConfiguration(this IApplicationBuilder app)
{
var appId = StartupHelper.GetApplicationId();
if (app is WebApplication webApp && webApp.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that develperexeptionpage gets added by default in net6 and forwads

Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
60.5% Coverage on New Code (required ≥ 65%)
20.0% Condition Coverage on New Code (required ≥ 65%)

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Label Pull requests with new features. Used when generation releasenotes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants