Skip to content

Commit

Permalink
Fix EmbeddeResource Path in Linux
Browse files Browse the repository at this point in the history
  • Loading branch information
Vu0r1-sec committed Sep 4, 2024
1 parent b88b7d7 commit b043748
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 29 deletions.
2 changes: 1 addition & 1 deletion IconCaptcha/IconCaptcha.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

<ItemGroup>
<!-- Embed default icons -->
<EmbeddedResource Include="Assets\**\*.png" LogicalName="%(RecursiveDir)%(Filename)%(Extension)"/>
<EmbeddedResource Include="Assets\**\*.png"/>
</ItemGroup>

<ItemGroup>
Expand Down
70 changes: 42 additions & 28 deletions IconCaptcha/IconCaptchaService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ public class IconCaptchaService
private const string CaptchaFieldId = "ic-hf-id";
private const string CaptchaFieldHoneypot = "ic-hf-hp";
private const string CaptchaFieldToken = "_iconcaptcha-token";

/// <summary>
/// The default length of a captcha token.
/// </summary>
private const int CaptchaTokenLength = 20;

/// <summary>
/// The default image width of a captcha challenge, in pixels.
/// </summary>
Expand Down Expand Up @@ -86,7 +86,7 @@ public class IconCaptchaService
["dark"] = new(Mode.dark, new byte[] { 64, 64, 64 }),
["legacy-dark"] = new(Mode.dark, new byte[] { 64, 64, 64 }),
};

private Random Rand { get; }

private ISessionProvider SessionProvider { get; }
Expand All @@ -101,7 +101,7 @@ public IconCaptchaService(ISessionProvider sessionProvider, IHttpContextAccessor
Options = options;
Rand = new Random();
}

private CaptchaSession _session;
private CaptchaSession Session
{
Expand All @@ -123,7 +123,7 @@ private CaptchaSession Session
return _session;
}
}

/// <summary>
/// Generates and returns a secure random string which will serve as a CSRF token for the current session. After
/// generating the token, it will be saved in the global session variable. The length of the token will be
Expand All @@ -135,9 +135,9 @@ private CaptchaSession Session
public string Token()
{
// Make sure to only generate a token if none exists.
if (Session.Token != null)
if (Session.Token != null)
return Session.Token;

// Create a secure captcha session token.
var bytes = RandomNumberGenerator.GetBytes(CaptchaTokenLength);
var token = Convert.ToHexString(bytes);
Expand Down Expand Up @@ -200,7 +200,7 @@ public CaptchaResult GetCaptchaData(Payload payload)
var iconPositions = new int[iconAmount];
var iconIds = new List<int>();
var correctIconId = -1;

// Create a random 'icon position' order.
var tempPositions = Enumerable.Range(0, iconAmount)
.OrderBy(c => Rand.Next())
Expand Down Expand Up @@ -262,7 +262,7 @@ public CaptchaResult GetCaptchaData(Payload payload)
Id = payload.CaptchaId
};
}

/// <summary>
/// Validates the user form submission. If the captcha is incorrect, it
/// will set the global error variable and return FALSE, else TRUE.
Expand Down Expand Up @@ -297,7 +297,7 @@ public void ValidateSubmission()
{
throw new IconCaptchaSubmissionException(6, Options.Value.Messages.FormToken);
}

// Initialize the session.
var sessionData = CreateSession(captchaId);

Expand Down Expand Up @@ -351,9 +351,9 @@ public bool ValidateToken(string payloadToken, string headerToken = null)
var options = Options.Value;

// Only validate if the token option is enabled.
if (!options.Token)
if (!options.Token)
return true;

var sessionToken = GetToken();

// If the token is empty but the option is enabled, the token was never requested.
Expand Down Expand Up @@ -385,9 +385,9 @@ public bool ValidateToken(string payloadToken, string headerToken = null)
/// <returns>TRUE if the correct icon was selected, FALSE if not.</returns>
public bool SetSelectedAnswer(Payload payload)
{
if (payload == null)
if (payload == null)
return false;

// Check if the captcha ID and required other payload data is set.
if (payload.CaptchaId == default || payload.XPos == null || payload.YPos == null || payload.Width == null)
{
Expand Down Expand Up @@ -488,11 +488,8 @@ public async Task GetImage(long identifier)
var themeIconColor = Options.Value.Themes[sessionData.Mode].Icons;
var iconPath = Path.Combine(iconsDirectoryPath, themeIconColor.ToString());

await using Stream placeholderStream = GetImageStream(isEmbedded, placeholder);
// Generate the captcha image.
await using var placeholderStream = isEmbedded
? GetType().Assembly.GetManifestResourceStream(placeholder)
: File.OpenRead(placeholder);

var generatedImage = GenerateImage(sessionData, iconPath, placeholderStream, isEmbedded);

// Set the content type header to the PNG MIME-type.
Expand All @@ -511,6 +508,27 @@ public async Task GetImage(long identifier)
await generatedImage.CopyToAsync(httpContext.Response.Body);
}

/// <summary>
/// Retrieves an image stream either from the file system or from embedded resources within the assembly.
/// </summary>
/// <param name="isEmbedded">Indicates whether the image is an embedded resource.</param>
/// <param name="file">The file path or resource name.</param>
/// <returns>A Stream representing the image.</returns>
/// <exception cref="FileNotFoundException">Thrown if the embedded resource is not found.</exception>
private Stream GetImageStream(bool isEmbedded, string file)
{
if (!isEmbedded)
return File.OpenRead(file);

// Format the resource name to match the embedded resource naming convention
var resourceRef = file.Replace('/', '.').Replace('\\', '.');
resourceRef = $"{GetType().Assembly.GetName().Name}.Assets.{resourceRef}";

// Retrieve the embedded resource stream or throw an exception if not found
return GetType().Assembly.GetManifestResourceStream(resourceRef)
?? throw new FileNotFoundException($"Resource '{resourceRef}' not found in embedded resources.");
}

/// <summary>
/// Returns a generated image containing the icons for the current captcha instance. The icons will be copied
/// onto a placeholder image, located at the $placeholderPath. The icons will be randomly rotated and flipped
Expand Down Expand Up @@ -539,11 +557,7 @@ private Stream GenerateImage(CaptchaChallenge challenge,
id =>
{
var icon = Path.Combine(iconPath, $"icon-{id}.png");

using var stream = embeddedFiles
? GetType().Assembly.GetManifestResourceStream(icon)
: File.OpenRead(icon);

using var stream = GetImageStream(embeddedFiles, icon);
return CreateImage(stream);
});

Expand Down Expand Up @@ -590,15 +604,15 @@ private Stream GenerateImage(CaptchaChallenge challenge,
if (rotateEnabled)
{
var degree = Rand.Next(1, 4);

// Only if the 'degree' is not the same as what it would already be at.
if (degree != 4)
{
var rotated = new SKBitmap(
degree % 2 == 0 ? icon.Width : icon.Height,
degree % 2 == 0 ? icon.Height : icon.Width
);

var surface = new SKCanvas(rotated);
surface.Translate(rotated.Width / 2, rotated.Height / 2);
surface.RotateDegrees(degree * 90);
Expand Down Expand Up @@ -661,7 +675,7 @@ private static SKBitmap CreateImage(Stream file)

return SKBitmap.FromImage(image);
}

/// <summary>
/// Returns the clicked icon position based on the X and Y position and the captcha width.
/// </summary>
Expand All @@ -679,7 +693,7 @@ private int DetermineClickedIcon(int clickedXPos, int clickedYPos, int captchaWi

return (int)Math.Floor(clickedXPos / ((decimal)captchaWidth / iconAmount));
}

/// <summary>
/// Calculates the amount of times 1 or more other icons can be present in the captcha image besides the correct icon.
/// Each other icons should be at least present 1 time more than the correct icon. When calculating the icon
Expand Down Expand Up @@ -749,7 +763,7 @@ private CaptchaChallenge CreateSession(long identifier = 0)

Session.Add(identifier, sessionData);
}

return sessionData;
}

Expand Down

0 comments on commit b043748

Please sign in to comment.