Skip to content

Commit 3da8176

Browse files
Merge pull request #255 from microsoft/sfi-code-fix
feat: Identity based Authentication implemented & Sfi code QL fixes for Comp Analysis
2 parents 4d7dc23 + 142d7ac commit 3da8176

File tree

17 files changed

+168
-80
lines changed

17 files changed

+168
-80
lines changed

Deployment/bicep/modules/azurecognitiveservice.bicep

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ resource cognitiveService 'Microsoft.CognitiveServices/accounts@2022-03-01' = {
1515
}
1616
kind: 'FormRecognizer'
1717
properties: {
18+
customSubDomainName: cognitiveServiceName
1819
}
1920
}
2021

Deployment/bicep/modules/azureopenaiservice.bicep

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ resource openAIService 'Microsoft.CognitiveServices/accounts@2023-05-01' = {
1212
name: 'S0'
1313
}
1414
properties: {
15+
customSubDomainName: openAIServiceName
1516
// Add any specific properties if needed
1617
}
1718
}

Deployment/bicep/modules/azuresearch.bicep

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ resource searchService 'Microsoft.Search/searchServices@2023-11-01' = {
1313
properties: {
1414
replicaCount: 1
1515
partitionCount: 1
16+
disableLocalAuth: true
1617
}
1718
}
1819

Deployment/scripts/deployAzureResources.ps1

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,6 @@ class DeploymentResult {
180180
[string]$StorageAccountConnectionString
181181
[string]$AzSearchServiceName
182182
[string]$AzSearchServicEndpoint
183-
[string]$AzSearchAdminKey
184183
[string]$LogicAppDocumentProcessWatcherName
185184
[string]$LogicAppBenchmarkProcessWatcherName
186185
[string]$LogicAppGapAnalysisProcessWatcherName
@@ -190,7 +189,6 @@ class DeploymentResult {
190189
[string]$AksName
191190
[string]$ContainerRegistryName
192191
[string]$AzCognitiveServiceName
193-
[string]$AzCognitiveServiceKey
194192
[string]$AzCognitiveServiceEndpoint
195193
[string]$AzOpenAiServiceName
196194
[string]$AzGPT4oModelName
@@ -200,7 +198,6 @@ class DeploymentResult {
200198
[string]$AzGPTEmbeddingModelName
201199
[string]$AzGPTEmbeddingModelId
202200
[string]$AzOpenAiServiceEndpoint
203-
[string]$AzOpenAiServiceKey
204201
[string]$AzCosmosDBName
205202
[string]$AzCosmosDBConnectionString
206203

@@ -213,7 +210,6 @@ class DeploymentResult {
213210
# Azure Search
214211
$this.AzSearchServiceName = ""
215212
$this.AzSearchServicEndpoint = ""
216-
$this.AzSearchAdminKey = ""
217213
# Logic Apps
218214
$this.LogicAppDocumentProcessWatcherName = ""
219215
$this.LogicAppBenchmarkProcessWatcherName = ""
@@ -228,11 +224,9 @@ class DeploymentResult {
228224
# Cognitive Service - Azure AI Intelligence Document Service
229225
$this.AzCognitiveServiceName = ""
230226
$this.AzCognitiveServiceEndpoint = ""
231-
$this.AzCognitiveServiceKey = ""
232227
# Open AI Service
233228
$this.AzOpenAiServiceName = ""
234229
$this.AzOpenAiServiceEndpoint = ""
235-
$this.AzOpenAiServiceKey = ""
236230
# Model - GPT4o
237231
$this.AzGPT4oModelName = ""
238232
$this.AzGPT4oModelId = ""
@@ -341,12 +335,6 @@ try {
341335
$deploymentResult.AzLogicAppGapAnalysisProcessWatcherUrl = Get-LogicAppTriggerUrl -subscriptionId $subscriptionID -resourceGroupName $deploymentResult.ResourceGroupName -logicAppName $deploymentResult.LogicAppGapAnalysisProcessWatcherName -triggerName $triggerName
342336
# Get MongoDB connection string
343337
$deploymentResult.AzCosmosDBConnectionString = az cosmosdb keys list --name $deploymentResult.AzCosmosDBName --resource-group $deploymentResult.ResourceGroupName --type connection-strings --query "connectionStrings[0].connectionString" -o tsv
344-
# Get Azure Cognitive Service API Key
345-
$deploymentResult.AzCognitiveServiceKey = az cognitiveservices account keys list --name $deploymentResult.AzCognitiveServiceName --resource-group $deploymentResult.ResourceGroupName --query "key1" -o tsv
346-
# Get Azure Search Service Admin Key
347-
$deploymentResult.AzSearchAdminKey = az search admin-key show --service-name $deploymentResult.AzSearchServiceName --resource-group $deploymentResult.ResourceGroupName --query "primaryKey" -o tsv
348-
# Get Azure Open AI Service API Key
349-
$deploymentResult.AzOpenAiServiceKey = az cognitiveservices account keys list --name $deploymentResult.AzOpenAiServiceName --resource-group $deploymentResult.ResourceGroupName --query "key1" -o tsv
350338

351339
######################################################################################################################
352340
# Step 3 : Update App Configuration files with Secrets and information for AI Service and Kernel Memory Service.
@@ -358,7 +346,6 @@ try {
358346

359347
$aiServicePlaceholders = @{
360348
'{{ azureopenaiendpoint }}' = $deploymentResult.AzOpenAiServiceEndpoint
361-
'{{ azureopenaiapikey }}' = $deploymentResult.AzOpenAiServiceKey
362349
'{{ azuregpt4omodelname }}' = $deploymentResult.AzGPT4oModelName
363350
'{{ azuregpt4omodelId }}' = $deploymentResult.AzGPT4oModelId
364351
'{{ gpt4-32Kembeddingmodelname }}' = $deploymentResult.AzGPTEmbeddingModelName
@@ -385,12 +372,9 @@ try {
385372
'{{ blobstorageName }}' = $deploymentResult.StorageAccountName
386373
'{{ blobstorageconnectionstring }}' = $deploymentResult.StorageAccountConnectionString
387374
'{{ azuresearchendpoint }}' = $deploymentResult.AzSearchServicEndpoint
388-
'{{ azuresearchadminkey }}' = $deploymentResult.AzSearchAdminKey
389375
'{{ azureopenaiendpoint }}' = $deploymentResult.AzOpenAiServiceEndpoint
390-
'{{ azureopenaiapikey }}' = $deploymentResult.AzOpenAiServiceKey
391376
'{{ azuregpt4omodelname }}' = $deploymentResult.AzGPT4oModelName
392377
'{{ embeddingmodelname }}' = $deploymentResult.AzGPTEmbeddingModelName
393-
'{{ azurecognitiveservicapikey }}' = $deploymentResult.AzCognitiveServiceKey
394378
'{{ azurecognitiveserviceendpoint }}' = $deploymentResult.AzCognitiveServiceEndpoint
395379
}
396380

@@ -515,12 +499,30 @@ try {
515499
$systemAssignedIdentity = $(az vmss identity assign --resource-group $vmssResourceGroupName --name $vmssName --query systemAssignedIdentity --output tsv)
516500

517501
# Assign the role for aks system assigned managed identity to Azure blob Storage Data contributor role with the scope of the storage account
502+
Write-Host "Assign the role for aks system assigned managed identity to Azure blob Storage Data contributor role" -ForegroundColor Green
518503
az role assignment create --role "Storage Blob Data Contributor" --assignee $systemAssignedIdentity --scope "/subscriptions/$subscriptionID/resourceGroups/$($deploymentResult.ResourceGroupName)/providers/Microsoft.Storage/storageAccounts/$($deploymentResult.StorageAccountName)"
519504

520505
# Assigne the role for aks system assigned managed identity to Azure queue data contributor role with the scope of the storage account
506+
Write-Host "Assign the role for aks system assigned managed identity to Azure queue data contributor role" -ForegroundColor Green
521507
az role assignment create --role "Storage Queue Data Contributor" --assignee $systemAssignedIdentity --scope "/subscriptions/$subscriptionID/resourceGroups/$($deploymentResult.ResourceGroupName)/providers/Microsoft.Storage/storageAccounts/$($deploymentResult.StorageAccountName)"
522508

509+
# Assign the role for aks system assigned managed identity to Azure Queue Data Contributor role with the scope of Storage Account
510+
Write-Host "Assign the role for aks system assigned managed identity to App Cognitive Services OpenAI User role" -ForegroundColor Green
511+
az role assignment create --assignee $systemAssignedIdentity --role "Cognitive Services OpenAI User" --scope "/subscriptions/$subscriptionID/resourceGroups/$($deploymentResult.ResourceGroupName)/providers/Microsoft.CognitiveServices/accounts/$($deploymentResult.AzOpenAiServiceName)"
523512

513+
# Assign the role for aks system assigned managed identity to Azure Queue Data Contributor role with the scope of Storage Account
514+
Write-Host "Assign the role for aks system assigned managed identity to App Search Index Data Contributor role" -ForegroundColor Green
515+
az role assignment create --assignee $systemAssignedIdentity --role "Search Index Data Contributor" --scope "/subscriptions/$subscriptionID/resourceGroups/$($deploymentResult.ResourceGroupName)/providers/Microsoft.Search/searchServices/$($deploymentResult.AzSearchServiceName)"
516+
517+
# Assign the role for aks system assigned managed identity to Azure Queue Data Contributor role with the scope of Storage Account
518+
Write-Host "Assign the role for aks system assigned managed identity to App Search Service Contributor role" -ForegroundColor Green
519+
az role assignment create --assignee $systemAssignedIdentity --role "Search Service Contributor" --scope "/subscriptions/$subscriptionID/resourceGroups/$($deploymentResult.ResourceGroupName)/providers/Microsoft.Search/searchServices/$($deploymentResult.AzSearchServiceName)"
520+
521+
# Assign the role for aks system assigned managed identity to Azure Queue Data Contributor role with the scope of Storage Account
522+
Write-Host "Assign the role for aks system assigned managed identity to App Cognitive Services User role" -ForegroundColor Green
523+
524+
az role assignment create --assignee $systemAssignedIdentity --role "Cognitive Services User" --scope "/subscriptions/$subscriptionID/resourceGroups/$($deploymentResult.ResourceGroupName)/providers/Microsoft.CognitiveServices/accounts/$($deploymentResult.AzCognitiveServiceName)"
525+
524526
# Update aks nodepools to updated new role
525527
try {
526528
Write-Host "Upgrading node pools..." -ForegroundColor Cyan

Services/appconfig/aiservice/appsettings.dev.json.template

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
"ModelName": "{{ azuregpt4omodelname }}",
1010
"ModelId": "{{ azuregpt4omodelId }}",
1111
"EndPoint": "{{ azureopenaiendpoint }}",
12-
"Key": "{{ azureopenaiapikey }}",
1312
"EmbeddingModelName": "{{ gpt4-32Kembeddingmodelname }}"
1413
},
1514
"ConnectionStrings": {

Services/appconfig/kernelmemory/appsettings.development.json.template

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,8 @@
124124
"VirtualHost": "/"
125125
},
126126
"AzureAISearch": {
127-
"Auth": "ApiKey",
127+
"Auth": "AzureIdentity",
128128
"Endpoint": "{{ azuresearchendpoint }}",
129-
"APIKey": "{{ azuresearchadminkey }}"
130129
},
131130
"Postgres": {
132131
"ConnectionString": "Host=localhost;Port=5432;Username=public;Password=",
@@ -155,18 +154,16 @@
155154
},
156155
"AzureOpenAIText": {
157156
"APIType": "ChatCompletion",
158-
"Auth": "ApiKey",
157+
"Auth": "AzureIdentity",
159158
"Endpoint": "{{ azureopenaiendpoint }}",
160-
"APIKey": "{{ azureopenaiapikey }}",
161159
"Deployment": "{{ azuregpt4omodelname }}",
162160
"MaxRetries": 10,
163161
"MaxTokenTotal": 32768,
164162
"TextModelMaxTokenTotal": 32768
165163
},
166164
"AzureOpenAIEmbedding": {
167-
"Auth": "ApiKey",
165+
"Auth": "AzureIdentity",
168166
"Endpoint": "{{ azureopenaiendpoint }}",
169-
"APIKey": "{{ azureopenaiapikey }}",
170167
"Deployment": "{{ embeddingmodelname }}",
171168
"MaxTokenTotal": 8191,
172169
"APIType": "EmbeddingGeneration"
@@ -187,8 +184,7 @@
187184
"MaxTokenTotal": 4096
188185
},
189186
"AzureAIDocIntel": {
190-
"Auth": "ApiKey",
191-
"APIKey": "{{ azurecognitiveservicapikey }}",
187+
"Auth": "AzureIdentity",
192188
"Endpoint": "{{ azurecognitiveserviceendpoint }}"
193189
}
194190
}

Services/src/esg-ai-doc-analysis/CFS.SK.Sustainability.AI.Host/ESRSHttpServiceMapper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public static void UseESRSEndpoint(this WebApplication app)
115115
var content = new StringContent(serializedParamJson, Encoding.UTF8, "application/json");
116116

117117
//Invoke Process Watcher
118-
var response = await appContext.httpClient.PostAsync(config["BenchmarkProcessing:processwatcherUrl"], content);
118+
var response = await appContext.httpClient.PostAsync(config["BenchmarkProcessing:processwatcherUrl"], content); // CodeQL [SM03781] We are reading this value from appsettings.json, this is not an user input
119119

120120
return Results.Accepted(locationUrl, benchmarkServiceRequest);
121121
}

Services/src/esg-ai-doc-analysis/CFS.SK.Sustainability.AI/DocumentManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ async public Task<DocumentServiceResult> RegisterDocumentFromFileUrl(RegisterDoc
177177
//Download file from URL then take only fileName from URL
178178
//the file location URL in the document will be mixed with SAS token to get it.
179179
//sample url - https://microsoft.seismic.com/app?ContentId=d6e9f9bb-70d4-4845-a2ad-dd25ecc343d6#/doccenter/a5266a70-9230-4c1e-a553-c5bddcb7a896/doc/%252Fdde0caec0e-9236-f21b-2991-5868e63d3984%252FdfYTZjNDRiZDMtMzEwZS1kNWZkLTNjOGEtNjliYWJjMjhmMmUw%252CPT0%253D%252CUGl0Y2ggRGVjaw%253D%253D%252Flffb13c1f1-d960-4bbe-8685-000afbf5a67f//?mode=view&parentPath=sessionStorage
180-
180+
serviceRequest.FileUrl = FileNameValidator.ValidateAndReturnSafeFileName(serviceRequest.FileUrl);
181181
var fileBytes = await this.httpClient.GetByteArrayAsync(serviceRequest.FileUrl);
182182
//write ByteArray to Stream
183183
MemoryStream downloadedFileMemoryStream = new MemoryStream(fileBytes);

Services/src/esg-ai-doc-analysis/CFS.SK.Sustainability.AI/ESRSGapAnalysisManager.cs

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
using Azure.Identity;
2626
using System.Text.RegularExpressions;
2727

28-
2928
namespace CFS.SK.Sustainability.AI
3029
{
3130
public class ESRSGapAnalysisManager : SemanticKernelLogicBase<ESRSGapAnalysisManager>
@@ -36,10 +35,6 @@ public class ESRSGapAnalysisManager : SemanticKernelLogicBase<ESRSGapAnalysisMan
3635
private readonly GapAnalysisJobRepository _gapAnalysisJobRepository;
3736
private readonly IConfiguration _config;
3837

39-
// Regex to validate file names (only allows letters, digits, dot, dash, underscore)
40-
private static readonly Regex ValidFileNameRegex =
41-
new Regex(@"^[A-Za-z0-9._\-]+$", RegexOptions.Compiled);
42-
4338
public ESRSGapAnalysisManager(ApplicationContext appContext,
4439
ILogger<ESRSGapAnalysisManager> logger,
4540
ESRSDisclosureRetriever esrsDisclosureRetriever,
@@ -209,7 +204,7 @@ public async Task<GapAnalysisJob> RegisterJob(GapAnalysisServiceRequest jobReque
209204
var fileName = $"GAPAnalysisReport-{sanitizedDisclosureNumber}-{jobId}";
210205

211206
// validate file name
212-
fileName = ValidateAndReturnSafeFileName(fileName);
207+
fileName = FileNameValidator.ValidateAndReturnSafeFileName(fileName);
213208
var blobClient = blobContainerClient.GetBlobClient($"{fileName}.md");
214209
using (Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(analysis_resultString)))
215210
{
@@ -223,7 +218,7 @@ public async Task<GapAnalysisJob> RegisterJob(GapAnalysisServiceRequest jobReque
223218
//Create BlobClient
224219
var htmlFileName = $"{fileName}.html";
225220
// validate html file name
226-
htmlFileName = ValidateAndReturnSafeFileName(htmlFileName);
221+
htmlFileName = FileNameValidator.ValidateAndReturnSafeFileName(htmlFileName);
227222
blobClient = blobContainerClient.GetBlobClient(htmlFileName);
228223
byte[] byteArray_html = MarkdownHtmlConverter.Convert(analysis_resultString);
229224

@@ -241,7 +236,7 @@ public async Task<GapAnalysisJob> RegisterJob(GapAnalysisServiceRequest jobReque
241236
//Convert HTML to PDF
242237
//Need to extra work for convert html to pdf
243238
var pdfFileName = $"{fileName}.pdf";
244-
pdfFileName = ValidateAndReturnSafeFileName(pdfFileName); // Validate pdffilename
239+
pdfFileName = FileNameValidator.ValidateAndReturnSafeFileName(pdfFileName); // Validate pdffilename
245240
HtmlPdfConverter.Convert(htmlFileName, pdfFileName);
246241

247242
//if Pdf file is exist then upload to the blob
@@ -271,7 +266,7 @@ public async Task<GapAnalysisJob> RegisterJob(GapAnalysisServiceRequest jobReque
271266
}
272267

273268
var metaDataFileName = $"GAPAnalysisReport-{sanitizedDisclosureNumber}-{jobId}-meta.json";
274-
metaDataFileName = ValidateAndReturnSafeFileName(metaDataFileName);// Validate metadatafilename
269+
metaDataFileName = FileNameValidator.ValidateAndReturnSafeFileName(metaDataFileName);// Validate metadatafilename
275270
blobClient = blobContainerClient.GetBlobClient(metaDataFileName);
276271

277272
using (Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(disclosureDescription))))
@@ -310,22 +305,5 @@ public async Task<GapAnalysisJob> RegisterJob(GapAnalysisServiceRequest jobReque
310305
return gapAnalysis_response;
311306
//return result.GetValue<string>();
312307
}
313-
314-
// Validate simple file name to prevent path traversal attacks
315-
// Returns the validated filename to help static analysis tools track sanitization
316-
private static string ValidateAndReturnSafeFileName(string name)
317-
{
318-
if (string.IsNullOrWhiteSpace(name))
319-
throw new ArgumentException("File name is empty or null.");
320-
321-
if (name.Contains("..") || name.Contains("/") || name.Contains("\\"))
322-
throw new ArgumentException("Invalid file name (contains path components or traversal).");
323-
324-
// Whitelist chars: letters, digits, dash, underscore, dot
325-
if (!ValidFileNameRegex.IsMatch(name))
326-
throw new ArgumentException("Invalid file name.");
327-
328-
return name;
329-
}
330308
}
331309
}

0 commit comments

Comments
 (0)