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

Aws credentials #128

Merged
merged 8 commits into from
Jan 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
147 changes: 147 additions & 0 deletions Src/Plugins/Security/AwsCredentialsValidator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;

using Amazon.IdentityManagement;
using Amazon.IdentityManagement.Model;
using Amazon.IdentityManagement.Model.Internal.MarshallTransformations;
using Amazon.Runtime;

namespace Microsoft.CodeAnalysis.Sarif.PatternMatcher.Plugins.Security
{
internal class AwsCredentialsValidator : ValidatorBase
{
internal static AwsCredentialsValidator Instance = new AwsCredentialsValidator();

public static string IsValidStatic(ref string matchedPattern,
ref Dictionary<string, string> groups,
ref string failureLevel,
ref string fingerprint,
ref string message)
{
return ValidatorBase.IsValidStatic(Instance,
ref matchedPattern,
ref groups,
ref failureLevel,
ref fingerprint,
ref message);
}

public static string IsValidDynamic(ref string fingerprint, ref string message)
{
return ValidatorBase.IsValidDynamic(Instance,
ref fingerprint,
ref message);
}

protected override string IsValidStaticHelper(ref string matchedPattern,
ref Dictionary<string, string> groups,
ref string failureLevel,
ref string fingerprintText,
ref string message)
{
string id = groups["id"];
string key = groups["key"];

fingerprintText = new Fingerprint
{
Id = id,
Key = key,
}.ToString();

return nameof(ValidationState.Unknown);
}

protected override string IsValidDynamicHelper(ref string fingerprintText,
ref string message)
{
var fingerprint = new Fingerprint(fingerprintText);

string id = fingerprint.Id;
string key = fingerprint.Key;

try
{
var iamClient = new AmazonIdentityManagementServiceClient(id, key);

GetAccountAuthorizationDetailsRequest request;
GetAccountAuthorizationDetailsResponse response;

GetUserRequest userRequest = new GetUserRequest();
GetUserResponse userResponse = iamClient.GetUserAsync().GetAwaiter().GetResult();

request = new GetAccountAuthorizationDetailsRequest();
response = iamClient.GetAccountAuthorizationDetailsAsync(request).GetAwaiter().GetResult();

message = BuildAuthorizedMessage(id, response);
}
catch (AmazonIdentityManagementServiceException e)
{
switch (e.ErrorCode)
{
case "InvalidClientTokenId":
case "SignatureDoesNotMatch":
{
return nameof(ValidationState.NoMatch);
}
}

message = $"An unexpected exception was caught attempting to authenticate AWS id '{id}': {e.Message}";
return nameof(ValidationState.Unknown);
}
catch (Exception e)
{
message = $"An unexpected exception was caught attempting to authentic AWS id '{id}': {e.Message}";
return nameof(ValidationState.Unknown);
}

/* var client = new HttpClient();

try
{
string uri = "https://iam.amazonaws.com/?Action=GetAccountAuthorizationDetails" +
"?X-Amz-Algorithm=AWS4-HMAC-SHA256" +
$"&X-Amz-Credential={id}";

HttpResponseMessage response = client.GetAsync(uri).GetAwaiter().GetResult();

switch (response.StatusCode)
{
case HttpStatusCode.Forbidden:
{
message = $"for AWS credential id '{id}'.";
return nameof(ValidationState.Unauthorized);
}
}
}
catch (Exception e)
{
message = $"An unexpected exception was caught attempting to authentic AWS id '{id}': {e.Message}";
return nameof(ValidationState.Unknown);
}
*/
return nameof(ValidationState.Authorized);
}

private string BuildAuthorizedMessage(string id, GetAccountAuthorizationDetailsResponse response)
{
var policyNames = new List<string>();

foreach (ManagedPolicyDetail policy in response.Policies)
{
policyNames.Add(policy.PolicyName);
}

string policyNamesText = string.Join(", ", policyNames);
return $"id '{id}' is authorized for role policies '{policyNamesText}'.";
}
}
}
6 changes: 3 additions & 3 deletions Src/Plugins/Security/SEC101.SecurePlaintextSecrets.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@
},
{
"Id": "SEC101/007",
"Name": "DoNotExposePlaintextSecrets/AWSAccessKeyIDValue",
"ContentsRegex": "\\b(?<refine>(A3T[0-9A-Z]|AKIA|AGPA|AROA|AIPA|ANPA|ANVA|ASIA)[0-9A-Z]{16})",
"MessageArguments": { "secretKind": "AWS Access Key ID Value" }
"Name": "DoNotExposePlaintextSecrets/AwsCredentials",
"ContentsRegex": "$AwsCredentials",
"MessageArguments": { "secretKind": "AWS access key and secret" }
},
{
"Id": "SEC101/008",
Expand Down
1 change: 1 addition & 0 deletions Src/Plugins/Security/Security.SharedStrings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@
$BinaryFiles=(?i)\.(bmp|dll|exe|gif|jpe?g|lock|pack|png|psd|tar\.gz|tiff?|ttf|xcf|zip)$
$SourceFiles=(?i)\.(azure|bat|c|cmd|config|cpp|cs|cscfg|definitions|dtsx|h|hxx|hpp|ini|java|jsx?|json|keys|kt|loadtest|m|md|php|properties|ps1|psm1|pubxml|py|resx|sample|sql|ste|swift|test|tsx?|txt|waz|xml)$

$AwsCredentials=(?s)\b(?<id>(A3T[0-9A-Z]|AKIA|AGPA|AROA|AIPA|ANPA|ANVA|ASIA)[0-9A-Z]{16}).+?\b(?i)(?<key>[0-9a-z\/+]{40})
$SlackToken=\b(?<refine>xox(?<type>p|b|a|o|r|s)-(?i)[0-9a-z\-]+)

1 change: 1 addition & 0 deletions Src/Plugins/Security/Security.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="AWSSDK.IdentityManagement" Version="3.5.0.61" />
<PackageReference Include="System.Text.Json" Version="5.0.1" />
</ItemGroup>

Expand Down
1 change: 0 additions & 1 deletion Src/Plugins/Security/SlackTokenValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
{
"$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.5.json",
"version": "2.1.0",
"runs": [
{
"tool": {
"driver": {
"name": "testhost",
"organization": "Microsoft Corporation",
"product": "Microsoft.TestHost",
"fullName": "testhost 15.0.0.0",
"version": "15.0.0.0",
"semanticVersion": "15.0.0",
"rules": [
{
"id": "SEC101/007",
"name": "DoNotExposePlaintextSecrets/AwsCredentials",
"fullDescription": {
"text": "Do not expose plaintext (or base64-encoded plaintext) secrets in versioned engineering content."
},
"messageStrings": {
"NotApplicable_InvalidMetadata": {
"text": "'{0}' was not evaluated for check '{1}' because the analysis is not relevant for the following reason: {2}."
},
"Default": {
"text": "'{0}' contains {1}{2}{3}{4}{5}."
}
},
"helpUri": "https://github.com/microsoft/sarif-pattern-matcher"
}
]
}
},
"invocations": [
{
"executionSuccessful": true
}
],
"results": [
{
"ruleId": "SEC101/007",
"ruleIndex": 0,
"message": {
"id": "Default",
"arguments": [
"SEC101_007.AwsCredentials.txt",
"an apparent ",
"",
"AWS access key and secret",
"",
" (no validation occurred as it was not enabled. Pass '--dynamic-validation' on the command-line to validate this match)"
]
},
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "src/Plugins/Tests.Security/TestData/SecurePlaintextSecrets/Inputs/SEC101_007.AwsCredentials.txt",
"uriBaseId": "SRC_ROOT"
},
"region": {
"startLine": 1,
"startColumn": 16,
"endLine": 2,
"endColumn": 60,
"charOffset": 15,
"charLength": 81,
"snippet": {
"text": "AKIAIOSFODNN7EXAMPLE\r\nAWSSecretAccessKey\twJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}
}
}
}
],
"fingerprints": {
"ValidationFingerprint/v1": "[id=AKIAIOSFODNN7EXAMPLE][key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY]"
}
}
],
"columnKind": "utf16CodeUnits"
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"message": {
"id": "Default",
"arguments": [
"SEC101.AzureDevOpsPersonalAccessToken_pats.txt",
"SEC101_102.AdoPat.txt",
"an apparent ",
"base64-encoded",
"Azure DevOps personal access token",
Expand All @@ -55,7 +55,7 @@
{
"physicalLocation": {
"artifactLocation": {
"uri": "src/Plugins/Tests.Security/TestData/SecurePlaintextSecrets/Inputs/SEC101.AzureDevOpsPersonalAccessToken_pats.txt",
"uri": "src/Plugins/Tests.Security/TestData/SecurePlaintextSecrets/Inputs/SEC101_102.AdoPat.txt",
"uriBaseId": "SRC_ROOT"
},
"region": {
Expand All @@ -82,7 +82,7 @@
"message": {
"id": "Default",
"arguments": [
"SEC101.AzureDevOpsPersonalAccessToken_pats.txt",
"SEC101_102.AdoPat.txt",
"an apparent ",
"",
"Azure DevOps personal access token",
Expand All @@ -94,7 +94,7 @@
{
"physicalLocation": {
"artifactLocation": {
"uri": "src/Plugins/Tests.Security/TestData/SecurePlaintextSecrets/Inputs/SEC101.AzureDevOpsPersonalAccessToken_pats.txt",
"uri": "src/Plugins/Tests.Security/TestData/SecurePlaintextSecrets/Inputs/SEC101_102.AdoPat.txt",
"uriBaseId": "SRC_ROOT"
},
"region": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
AWSAccessKeyId AKIAIOSFODNN7EXAMPLE
AWSSecretAccessKey wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
4 changes: 2 additions & 2 deletions Src/Plugins/Tests.Security/Tests.Security.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
</ItemGroup>

<ItemGroup>
<None Remove="TestData\SecurePlaintextSecrets\ExpectedOutputs\SEC101_007.AwsCredentials.sarif" />
<None Remove="TestData\SecurePlaintextSecrets\Inputs\SEC101_007.AwsCredentials.txt" />
<None Remove="TestData\UseSecureApi\ExpectedOutputs\SEC101_005.SlackTokens.sarif" />
<None Remove="TestData\UseSecureApi\Inputs\SEC104.Memory.Allocation_alloca.c" />
</ItemGroup>
Expand All @@ -26,6 +28,4 @@
<ProjectReference Include="..\..\sarif-sdk\src\Test.Utilities.Sarif\Test.Utilities.Sarif.csproj" />
</ItemGroup>

<ProjectExtensions><VisualStudio><UserProperties testdata_4reviewpotentiallysensitivefiles_4expectedoutputs_4sec103_1certificatefile_3cer_3encoded_1sarif__JsonSchema="" /></VisualStudio></ProjectExtensions>

</Project>
6 changes: 4 additions & 2 deletions Src/Plugins/ValidatorBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,13 @@ public static string ReturnUnknownHost(ref string message, string host)

public static string ReturnUnauthorizedAccess(ref string message,
string asset,
string assetIdentifier = null,
string account = null)
{
assetIdentifier += assetIdentifier != null ? " " : string.Empty;
message = (account == null) ?
$"The provided secret is not authorized to access '{asset}'." :
$"The provided '{account}' account secret is not authorized to access '{asset}'.";
$"The provided secret is not authorized to access {assetIdentifier}'{asset}'." :
$"The provided '{account}' account secret is not authorized to access {assetIdentifier}'{asset}'.";

return nameof(ValidationState.Unauthorized);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public async Task Function_HttpAnalyze_NormalFileContent_Should_Return_SarifLog(
[Fact]
public async Task Function_HttpAnalyze_FileWithPAT_Should_Return_SarifLog()
{
const string patTextFile = "SEC101.AzureDevOpsPersonalAccessToken_pats.txt";
const string patTextFile = "SEC101_102.AdoPat.txt";
IActionResult result = await HttpAnalyzeFunction.Analyze(
request: TestHelper.MockAnalyzeFunctionRequest(patTextFile, TestHelper.GetTestResourceContent(patTextFile)),
log: logger,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
</ItemGroup>

<ItemGroup>
<Content Include="..\Plugins\Tests.Security\TestData\SecurePlaintextSecrets\Inputs\SEC101.AzureDevOpsPersonalAccessToken_pats.txt" Link="TestData\SEC101.AzureDevOpsPersonalAccessToken_pats.txt" CopyToOutputDirectory="PreserveNewest" />
<Content Include="..\Plugins\Tests.Security\TestData\SecurePlaintextSecrets\Inputs\SEC101_102.AdoPat.txt" Link="TestData\SEC101_102.AdoPat.txt" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

<Target Name="CopySecFilesAfterBuild" AfterTargets="AfterBuild">
Expand Down