-
-
Save TimothyODonnell/26f1e6dbc8a50e1dd94a284336f14ecd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using nClam; | |
using System; | |
using Microsoft.Azure.WebJobs; | |
using Microsoft.Azure.WebJobs.Extensions.EventGrid; | |
using Microsoft.Extensions.Logging; | |
using Azure.Storage.Blobs; | |
using System.IO; | |
using System.Threading.Tasks; | |
using Newtonsoft.Json.Linq; | |
using System.Linq; | |
using System.Collections.Generic; | |
using System.Diagnostics.CodeAnalysis; | |
using Azure.Messaging.EventGrid; | |
using Azure; | |
using Azure.Storage.Blobs.Models; | |
namespace CLamAvSample.AzFunctions; | |
public static class RunAvOnFileUploaded | |
{ | |
public static int ClamAVServerPort = int.Parse(Environment.GetEnvironmentVariable("CLAMAV_SERVER_PORT", EnvironmentVariableTarget.Process) ?? throw new Exception("Port must be configured")); | |
public static string ConnectionString = Environment.GetEnvironmentVariable("ATTACHMENT_STORAGE_CONNSTRING", EnvironmentVariableTarget.Process) ?? throw new Exception("Connection string missing"); | |
public static string ClamAVServerUrl = Environment.GetEnvironmentVariable("CLAMAV_SERVER_URL", EnvironmentVariableTarget.Process) ?? throw new Exception("Connection string missing"); | |
const string CREATED_EVENT_URL = "url"; | |
private static bool TryGetFileNameAndContainerFromUrl(string url, [NotNullWhen(true)] out string? fileName, [NotNullWhen(true)] out string? container) | |
{ | |
var urlParts = url.Split('/'); | |
var urlFileName = urlParts.Last(); | |
fileName = string.IsNullOrEmpty(urlFileName) ? null : urlFileName; | |
container = urlParts.SkipLast(1).Last(); | |
bool isValid = !String.IsNullOrWhiteSpace(fileName) && !String.IsNullOrWhiteSpace(container); | |
return isValid; | |
} | |
private static bool TryGetEventData<T>(Microsoft.Azure.EventGrid.Models.EventGridEvent eventGridEvent, string property, [NotNullWhen(true)] out T? value) | |
{ | |
value = default(T); | |
var eventData = eventGridEvent.Data.ToString(); | |
if (eventData == null) return false; | |
var eventDataJson = JObject.Parse(eventData); | |
bool found = eventDataJson.TryGetValue(property, out var eventDataValue); | |
value = found ? eventDataValue!.Value<T>() : value; | |
return found; | |
} | |
[FunctionName("RunAvOnFileUploaded")] | |
public static async Task Run([EventGridTrigger] Microsoft.Azure.EventGrid.Models.EventGridEvent eventGridEvent, ILogger log) | |
{ | |
// ##### READ EVENT GRID EVENT FROM REQUEST ##### | |
log.LogInformation("Begin handling request. {EventGridEvent}", eventGridEvent.Data); | |
if (!TryGetEventData<string>(eventGridEvent, CREATED_EVENT_URL, out var url)) | |
{ | |
log.LogError("EventData could not be parsed. UrlFound check failed."); | |
return; | |
} | |
// ##### READ FILE NAME AND STORAGE ACCOUNT CONTAINER NAME FROM EVENT GRID EVENT | |
if (!TryGetFileNameAndContainerFromUrl(url, out var fileName, out var container)) | |
{ | |
log.LogError("Invalid file URI, could not process. Url value: {Url}", url); | |
return; | |
} | |
// ##### DOWNLOAD FILE FROM AZURE STORAGE ##### | |
log.LogInformation("Downloading {FileName} from container {Container}...", fileName, container); | |
var blobClient = new BlobClient(ConnectionString, container, fileName); | |
// ##### DOWNLOAD FILE ##### | |
using var ms = new MemoryStream(); | |
await blobClient.DownloadToAsync(ms); | |
// ##### RUN AV ON FILE ##### | |
log.LogInformation("Download completed. Connecting to AV server {ClamAVServerUrl}:{ClamAVServerPort}... ", ClamAVServerUrl, ClamAVServerPort); | |
var clam = new ClamClient(ClamAVServerUrl, ClamAVServerPort); | |
bool isConnected = await clam.PingAsync(); | |
var version = await clam.GetVersionAsync(); | |
if (!isConnected) throw new Exception("AV server connection could not be established."); | |
log.LogInformation("Connection ok: {IsConnected}. AV server reports version: {ServerVersion}", isConnected, version); | |
var scanResult = await clam.SendAndScanFileAsync(ms.ToArray()); | |
switch (scanResult.Result) | |
{ | |
case ClamScanResults.Clean: | |
log.LogInformation("The file is clean!"); | |
// Continue | |
break; | |
case ClamScanResults.VirusDetected: | |
log.LogCritical("Virus found. Deleting file."); | |
await blobClient.DeleteIfExistsAsync(Azure.Storage.Blobs.Models.DeleteSnapshotsOption.IncludeSnapshots); | |
// Do other steps | |
break; | |
case ClamScanResults.Error: | |
default: | |
log.LogInformation("File scan error occurred. {ScanResult}", scanResult.RawResult); | |
break; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment