mirror of
https://github.com/microsoft/BCQuality.git
synced 2026-08-06 17:36:53 +01:00
Address security privacy review findings
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9c2eebc4-dcd5-4b85-8113-90772d818900
This commit is contained in:
parent
ec8f891954
commit
d8f372453c
8 changed files with 14 additions and 14 deletions
|
|
@ -5,7 +5,7 @@ codeunit 50209 "Privacy Sample GetLastError Bad"
|
||||||
AttachmentFailedErr: Label 'Attachment failed: %1', Comment = '%1 = underlying error';
|
AttachmentFailedErr: Label 'Attachment failed: %1', Comment = '%1 = underlying error';
|
||||||
begin
|
begin
|
||||||
if not TryAddAttachment() then
|
if not TryAddAttachment() then
|
||||||
Error(StrSubstNo(AttachmentFailedErr, GetLastErrorText(true)));
|
Error(StrSubstNo(AttachmentFailedErr, GetLastErrorText()));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure AddAttachmentWithConcatenation()
|
procedure AddAttachmentWithConcatenation()
|
||||||
|
|
@ -13,7 +13,7 @@ codeunit 50209 "Privacy Sample GetLastError Bad"
|
||||||
AttachmentFailedErr: Label 'Attachment failed: ';
|
AttachmentFailedErr: Label 'Attachment failed: ';
|
||||||
begin
|
begin
|
||||||
if not TryAddAttachment() then
|
if not TryAddAttachment() then
|
||||||
Error(AttachmentFailedErr + GetLastErrorText(true));
|
Error(AttachmentFailedErr + GetLastErrorText());
|
||||||
end;
|
end;
|
||||||
|
|
||||||
[TryFunction]
|
[TryFunction]
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ codeunit 50208 "Privacy Sample GetLastError Good"
|
||||||
AttachmentFailedErr: Label 'Failed to add the attachment: %1', Comment = '%1 = underlying error shown to the user';
|
AttachmentFailedErr: Label 'Failed to add the attachment: %1', Comment = '%1 = underlying error shown to the user';
|
||||||
begin
|
begin
|
||||||
if not TryAddAttachment() then
|
if not TryAddAttachment() then
|
||||||
Error(AttachmentFailedErr, GetLastErrorText(true));
|
Error(AttachmentFailedErr, GetLastErrorText());
|
||||||
end;
|
end;
|
||||||
|
|
||||||
[TryFunction]
|
[TryFunction]
|
||||||
|
|
|
||||||
|
|
@ -11,16 +11,16 @@ application-area: [all]
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
`GetLastErrorText()` can contain customer content such as field values, record keys, and file names. When it is passed as a substitution value to an `Error` whose first argument is a `Label` or `TextConst`, the label supplies the Error method trace telemetry message. If `StrSubstNo` or concatenation makes `GetLastErrorText()` part of the first argument, the actual dynamic string is not emitted as that telemetry message; telemetry uses generic guidance instead.
|
Parameterless `GetLastErrorText()` can contain customer content such as field values, record keys, and file names. The Boolean overload names its parameter `ExcludeCustomerContent`; passing `true` requests scrubbed text and is not the customer-content scenario covered here. When unsanitized error text is passed as a substitution value to an `Error` whose first argument is a `Label` or `TextConst`, the label supplies the Error method trace telemetry message.
|
||||||
|
|
||||||
## Best Practice
|
## Best Practice
|
||||||
|
|
||||||
Use a generic label when the user does not need the underlying detail. If showing the detail is appropriate, put `%1` in a label and pass `GetLastErrorText()` as a separate argument. This preserves a useful static telemetry message while keeping the dynamic value out of the telemetry message field.
|
Use a generic label when the user does not need the underlying detail. If showing unsanitized detail is appropriate, put `%1` in a label and pass parameterless `GetLastErrorText()` as a separate argument. This preserves a useful static telemetry message while keeping the dynamic value out of the telemetry message field.
|
||||||
|
|
||||||
See sample: `getlasterrortext-customer-content-in-errors.good.al`.
|
See sample: `getlasterrortext-customer-content-in-errors.good.al`.
|
||||||
|
|
||||||
## Anti Pattern
|
## Anti Pattern
|
||||||
|
|
||||||
`Error(StrSubstNo(AttachmentFailedErr, GetLastErrorText(true)))` or `Error(AttachmentPrefixErr + GetLastErrorText(true))`. Both lose the static first argument and trigger AA0231; neither causes the composed text to be logged verbatim as the Error telemetry message.
|
`Error(StrSubstNo(AttachmentFailedErr, GetLastErrorText()))` or `Error(AttachmentPrefixErr + GetLastErrorText())`. Both lose the static first argument and trigger AA0231; neither causes the composed text to be logged verbatim as the Error telemetry message.
|
||||||
|
|
||||||
See sample: `getlasterrortext-customer-content-in-errors.bad.al`.
|
See sample: `getlasterrortext-customer-content-in-errors.bad.al`.
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ codeunit 50212 "Sec Sample SecretSubst Bad"
|
||||||
{
|
{
|
||||||
procedure BuildAuthHeader(Token: Text): Text
|
procedure BuildAuthHeader(Token: Text): Text
|
||||||
begin
|
begin
|
||||||
exit(StrSubstNo('Bearer %1', Token));
|
exit(StrSubstNo('Token %1', Token));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure BuildSecretUri(ApiKey: Text): Text
|
procedure BuildSecretUri(ApiKey: Text): Text
|
||||||
|
|
@ -12,6 +12,6 @@ codeunit 50212 "Sec Sample SecretSubst Bad"
|
||||||
|
|
||||||
procedure BuildBrokenAuthHeader(Token: SecretText): SecretText
|
procedure BuildBrokenAuthHeader(Token: SecretText): SecretText
|
||||||
begin
|
begin
|
||||||
exit(SecretStrSubstNo('Bearer', Token));
|
exit(SecretStrSubstNo('Token', Token));
|
||||||
end;
|
end;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ codeunit 50211 "Sec Sample SecretSubst Good"
|
||||||
{
|
{
|
||||||
procedure BuildAuthHeader(Token: SecretText): SecretText
|
procedure BuildAuthHeader(Token: SecretText): SecretText
|
||||||
begin
|
begin
|
||||||
exit(SecretStrSubstNo('Bearer %1', Token));
|
exit(SecretStrSubstNo('Token %1', Token));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure BuildSecretUri(ApiKey: SecretText): SecretText
|
procedure BuildSecretUri(ApiKey: SecretText): SecretText
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ application-area: [all]
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
`SecretStrSubstNo` is the secret-preserving counterpart of `StrSubstNo`. It inserts `SecretText` arguments into `%1`, `%2`, and similar placeholders and returns `SecretText` without materializing the result as plain text. It is the right tool for values such as a `Bearer %1` authorization header or a URI with an API key placeholder.
|
`SecretStrSubstNo` is the secret-preserving counterpart of `StrSubstNo`. It inserts `SecretText` arguments into `%1`, `%2`, and similar placeholders and returns `SecretText` without materializing the result as plain text. It is the right tool for values such as a `Token %1` authorization header or a URI with an API key placeholder.
|
||||||
|
|
||||||
## Best Practice
|
## Best Practice
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,14 +10,14 @@ codeunit 50210 "Sec Sample SecretHttp Bad"
|
||||||
HttpClient.Get(RequestUri, Response);
|
HttpClient.Get(RequestUri, Response);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure CallApiWithBearer(BearerToken: Text)
|
procedure CallApiWithAccessToken(AccessToken: Text)
|
||||||
var
|
var
|
||||||
HttpClient: HttpClient;
|
HttpClient: HttpClient;
|
||||||
Response: HttpResponseMessage;
|
Response: HttpResponseMessage;
|
||||||
Headers: HttpHeaders;
|
Headers: HttpHeaders;
|
||||||
begin
|
begin
|
||||||
Headers := HttpClient.DefaultRequestHeaders();
|
Headers := HttpClient.DefaultRequestHeaders();
|
||||||
Headers.Add('Authorization', StrSubstNo('Bearer %1', BearerToken));
|
Headers.Add('Authorization', StrSubstNo('Token %1', AccessToken));
|
||||||
HttpClient.Get('https://api.example.com/data', Response);
|
HttpClient.Get('https://api.example.com/data', Response);
|
||||||
end;
|
end;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ codeunit 50209 "Sec Sample SecretHttp Good"
|
||||||
HttpClient.Send(Request, Response);
|
HttpClient.Send(Request, Response);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure CallApiWithBearer(BearerToken: SecretText)
|
procedure CallApiWithAccessToken(AccessToken: SecretText)
|
||||||
var
|
var
|
||||||
HttpClient: HttpClient;
|
HttpClient: HttpClient;
|
||||||
Request: HttpRequestMessage;
|
Request: HttpRequestMessage;
|
||||||
|
|
@ -25,7 +25,7 @@ codeunit 50209 "Sec Sample SecretHttp Good"
|
||||||
Request.Method := 'GET';
|
Request.Method := 'GET';
|
||||||
Request.SetRequestUri('https://api.example.com/data');
|
Request.SetRequestUri('https://api.example.com/data');
|
||||||
Request.GetHeaders(Headers);
|
Request.GetHeaders(Headers);
|
||||||
AuthHeader := SecretStrSubstNo('Bearer %1', BearerToken);
|
AuthHeader := SecretStrSubstNo('Token %1', AccessToken);
|
||||||
Headers.Add('Authorization', AuthHeader);
|
Headers.Add('Authorization', AuthHeader);
|
||||||
if not Headers.ContainsSecret('Authorization') then
|
if not Headers.ContainsSecret('Authorization') then
|
||||||
Error(AuthorizationHeaderMissingErr);
|
Error(AuthorizationHeaderMissingErr);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue