Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/Gemstone/Configuration/INIConfigurationHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

using System;
using System.IO;
using Gemstone.IO;

namespace Gemstone.Configuration;

Expand All @@ -35,11 +36,15 @@
/// Gets file path for INI configuration file.
/// </summary>
/// <param name="fileName">Target file INI file name.</param>
/// <param name="configuredINIPath">Any configured path for the INI file.</param>
/// <returns>INI file path.</returns>
public static string GetINIFilePath(string fileName)
public static string GetINIFilePath(string fileName, string? configuredINIPath)
{
Environment.SpecialFolder specialFolder = Environment.SpecialFolder.CommonApplicationData;
string appDataPath = Environment.GetFolderPath(specialFolder);
if (!string.IsNullOrWhiteSpace(configuredINIPath))
return Path.Combine(FilePath.GetAbsolutePath(configuredINIPath), fileName);

Check notice

Code scanning / CodeQL

Call to 'System.IO.Path.Combine' may silently drop its earlier arguments Note

Call to 'System.IO.Path.Combine' may silently drop its earlier arguments.

Copilot Autofix

AI about 2 months ago

In general, to avoid Path.Combine silently dropping earlier arguments, avoid combining an absolute path with later arguments that might themselves be absolute. Either (a) normalize or validate the later segments to ensure they are not rooted, or (b) use Path.Join, which concatenates segments without the special absolute-path override behavior.

For this specific method, FilePath.GetAbsolutePath(configuredINIPath) already returns an absolute base directory, and fileName is conceptually a file name segment. The least intrusive, behavior-preserving fix is to change the flagged call from Path.Combine to Path.Join. Path.Join is available in System.IO (already imported) and will join the directory and file name without discarding the first argument even if fileName is absolute; the resulting path is then just the concatenation, which is safer and avoids the CodeQL warning. No other logic, signatures, or imports need to change.

Concretely, in src/Gemstone/Configuration/INIConfigurationHelpers.cs, update line 44 so that Path.Combine(FilePath.GetAbsolutePath(configuredINIPath), fileName) becomes Path.Join(FilePath.GetAbsolutePath(configuredINIPath), fileName). Leave the rest of the method and file unchanged.

Suggested changeset 1
src/Gemstone/Configuration/INIConfigurationHelpers.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/Gemstone/Configuration/INIConfigurationHelpers.cs b/src/Gemstone/Configuration/INIConfigurationHelpers.cs
--- a/src/Gemstone/Configuration/INIConfigurationHelpers.cs
+++ b/src/Gemstone/Configuration/INIConfigurationHelpers.cs
@@ -41,7 +41,7 @@
     public static string GetINIFilePath(string fileName, string? configuredINIPath)
     {
         if (!string.IsNullOrWhiteSpace(configuredINIPath))
-            return Path.Combine(FilePath.GetAbsolutePath(configuredINIPath), fileName);
+            return Path.Join(FilePath.GetAbsolutePath(configuredINIPath), fileName);
 
         const Environment.SpecialFolder SpecialFolder = Environment.SpecialFolder.CommonApplicationData;
         string appDataPath = Environment.GetFolderPath(SpecialFolder);
EOF
@@ -41,7 +41,7 @@
public static string GetINIFilePath(string fileName, string? configuredINIPath)
{
if (!string.IsNullOrWhiteSpace(configuredINIPath))
return Path.Combine(FilePath.GetAbsolutePath(configuredINIPath), fileName);
return Path.Join(FilePath.GetAbsolutePath(configuredINIPath), fileName);

const Environment.SpecialFolder SpecialFolder = Environment.SpecialFolder.CommonApplicationData;
string appDataPath = Environment.GetFolderPath(SpecialFolder);
Copilot is powered by AI and may make mistakes. Always verify output.

const Environment.SpecialFolder SpecialFolder = Environment.SpecialFolder.CommonApplicationData;
string appDataPath = Environment.GetFolderPath(SpecialFolder);
return Path.Combine(appDataPath, Common.ApplicationName, fileName);
}

Expand Down
40 changes: 12 additions & 28 deletions src/Gemstone/Configuration/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ public Settings()
/// </summary>
public ConfigurationOperation INIFile { get; init; } = ConfigurationOperation.ReadOnly;

/// <summary>
/// Gets or sets any configured INI path. Set to <c>null</c> for default %ProgramData% path.
/// </summary>
public string? ConfiguredINIPath { get; init; } = null;

/// <summary>
/// Gets or sets configuration operation mode for SQLite settings.
/// </summary>
Expand Down Expand Up @@ -161,24 +166,12 @@ public ConfigurationOperation EnvironmentalVariables
/// <summary>
/// Gets the names for the settings sections.
/// </summary>
public string[] SectionNames
{
get
{
return m_sections.Keys.ToArray();
}
}
public string[] SectionNames => m_sections.Keys.ToArray();

/// <summary>
/// Gets the sections count for the settings.
/// </summary>
public int Count
{
get
{
return m_sections.Count;
}
}
public int Count => m_sections.Count;

/// <summary>
/// Gets the command line switch mappings for <see cref="Settings"/>.
Expand All @@ -189,23 +182,14 @@ public int Count
/// Gets the <see cref="SettingsSection"/> for the specified key.
/// </summary>
/// <param name="key">Section key.</param>
public SettingsSection this[string key]
{
get
{
return m_sections.GetOrAdd(key, _ => new SettingsSection(this, key));
}
}
public SettingsSection this[string key] => m_sections.GetOrAdd(key, _ => new SettingsSection(this, key));

/// <summary>
/// Gets flag that determines if any settings have been changed.
/// </summary>
public bool IsDirty
{
get
{
return m_sections.Values.Any(section => section.IsDirty);
}
get => m_sections.Values.Any(section => section.IsDirty);
private set
{
foreach (SettingsSection section in m_sections.Values)
Expand Down Expand Up @@ -247,8 +231,8 @@ public void Bind(IConfigurationBuilder builder)

if (iniProviderExists)
{
string iniPath = GetINIFilePath("settings.ini");
using TextReader reader = GetINIFileReader(iniPath);
string iniFilePath = GetINIFilePath("settings.ini", ConfiguredINIPath);
using TextReader reader = GetINIFileReader(iniFilePath);
string[] iniFileContents = reader.ReadToEnd().Split(["\n", "\r", "\r\n"], StringSplitOptions.RemoveEmptyEntries);
string currentSection = "";
StringBuilder currentDescription = new();
Expand Down Expand Up @@ -440,7 +424,7 @@ private void SaveSections()

// Handle INI file as a special case, writing entire file contents on save
string contents = Configuration!.GenerateINIFileContents(splitDescriptionLines: SplitDescriptionLines);
string iniFilePath = GetINIFilePath("settings.ini");
string iniFilePath = GetINIFilePath("settings.ini", ConfiguredINIPath);
using TextWriter writer = GetINIFileWriter(iniFilePath);
writer.Write(contents);
}
Expand Down
Loading