diff options
author | Jesse Plamondon-Willard <github@jplamondonw.com> | 2018-08-18 23:33:38 -0400 |
---|---|---|
committer | Jesse Plamondon-Willard <github@jplamondonw.com> | 2018-08-18 23:33:38 -0400 |
commit | 944b2995f1bf7719cfcfb9bafe713523dbd8883f (patch) | |
tree | bc0a7f30af261f1aa109acb32e56dd8c3b0cb680 | |
parent | d918ceb224bd6ed8b428219ab28a436896a30b45 (diff) | |
download | SMAPI-944b2995f1bf7719cfcfb9bafe713523dbd8883f.tar.gz SMAPI-944b2995f1bf7719cfcfb9bafe713523dbd8883f.tar.bz2 SMAPI-944b2995f1bf7719cfcfb9bafe713523dbd8883f.zip |
no longer allow non-relative paths for IContentPack.Read/WriteJsonFile (#468)
-rw-r--r-- | docs/release-notes.md | 1 | ||||
-rw-r--r-- | src/SMAPI/Framework/ContentPack.cs | 8 | ||||
-rw-r--r-- | src/SMAPI/IContentPack.cs | 2 | ||||
-rw-r--r-- | src/StardewModdingAPI.Toolkit/Utilities/PathUtilities.cs | 12 |
4 files changed, 23 insertions, 0 deletions
diff --git a/docs/release-notes.md b/docs/release-notes.md index fc6ea97f..09d444a3 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -5,6 +5,7 @@ * For modders: * Added `IContentPack.WriteJsonFile` method. + * Fixed `IContentPack.ReadJsonFile` allowing non-relative paths. ## 2.7 * For players: diff --git a/src/SMAPI/Framework/ContentPack.cs b/src/SMAPI/Framework/ContentPack.cs index ccb2b9a0..49285388 100644 --- a/src/SMAPI/Framework/ContentPack.cs +++ b/src/SMAPI/Framework/ContentPack.cs @@ -51,8 +51,12 @@ namespace StardewModdingAPI.Framework /// <typeparam name="TModel">The model type.</typeparam> /// <param name="path">The file path relative to the contnet directory.</param> /// <returns>Returns the deserialised model, or <c>null</c> if the file doesn't exist or is empty.</returns> + /// <exception cref="InvalidOperationException">The <paramref name="path"/> is not relative or contains directory climbing (../).</exception> public TModel ReadJsonFile<TModel>(string path) where TModel : class { + if (!PathUtilities.IsSafeRelativePath(path)) + throw new InvalidOperationException($"You must call {nameof(IContentPack)}.{nameof(this.ReadJsonFile)} with a relative path."); + path = Path.Combine(this.DirectoryPath, PathUtilities.NormalisePathSeparators(path)); return this.JsonHelper.ReadJsonFileIfExists(path, out TModel model) ? model @@ -63,8 +67,12 @@ namespace StardewModdingAPI.Framework /// <typeparam name="TModel">The model type. This should be a plain class that has public properties for the data you want. The properties can be complex types.</typeparam> /// <param name="path">The file path relative to the mod folder.</param> /// <param name="data">The arbitrary data to save.</param> + /// <exception cref="InvalidOperationException">The <paramref name="path"/> is not relative or contains directory climbing (../).</exception> public void WriteJsonFile<TModel>(string path, TModel data) where TModel : class { + if (!PathUtilities.IsSafeRelativePath(path)) + throw new InvalidOperationException($"You must call {nameof(IContentPack)}.{nameof(this.WriteJsonFile)} with a relative path."); + path = Path.Combine(this.DirectoryPath, PathUtilities.NormalisePathSeparators(path)); this.JsonHelper.WriteJsonFile(path, data); } diff --git a/src/SMAPI/IContentPack.cs b/src/SMAPI/IContentPack.cs index fa793b13..9ba32394 100644 --- a/src/SMAPI/IContentPack.cs +++ b/src/SMAPI/IContentPack.cs @@ -25,12 +25,14 @@ namespace StardewModdingAPI /// <typeparam name="TModel">The model type. This should be a plain class that has public properties for the data you want. The properties can be complex types.</typeparam> /// <param name="path">The file path relative to the content pack directory.</param> /// <returns>Returns the deserialised model, or <c>null</c> if the file doesn't exist or is empty.</returns> + /// <exception cref="InvalidOperationException">The <paramref name="path"/> is not relative or contains directory climbing (../).</exception> TModel ReadJsonFile<TModel>(string path) where TModel : class; /// <summary>Save data to a JSON file in the content pack's folder.</summary> /// <typeparam name="TModel">The model type. This should be a plain class that has public properties for the data you want. The properties can be complex types.</typeparam> /// <param name="path">The file path relative to the mod folder.</param> /// <param name="data">The arbitrary data to save.</param> + /// <exception cref="InvalidOperationException">The <paramref name="path"/> is not relative or contains directory climbing (../).</exception> void WriteJsonFile<TModel>(string path, TModel data) where TModel : class; /// <summary>Load content from the content pack folder (if not already cached), and return it. When loading a <c>.png</c> file, this must be called outside the game's draw loop.</summary> diff --git a/src/StardewModdingAPI.Toolkit/Utilities/PathUtilities.cs b/src/StardewModdingAPI.Toolkit/Utilities/PathUtilities.cs index b959f9b5..79748c25 100644 --- a/src/StardewModdingAPI.Toolkit/Utilities/PathUtilities.cs +++ b/src/StardewModdingAPI.Toolkit/Utilities/PathUtilities.cs @@ -63,6 +63,18 @@ namespace StardewModdingAPI.Toolkit.Utilities return relative; } + /// <summary>Get whether a path is relative and doesn't try to climb out of its containing folder (e.g. doesn't contain <c>../</c>).</summary> + /// <param name="path">The path to check.</param> + public static bool IsSafeRelativePath(string path) + { + if (string.IsNullOrWhiteSpace(path)) + return true; + + return + !Path.IsPathRooted(path) + && PathUtilities.GetSegments(path).All(segment => segment.Trim() != ".."); + } + /// <summary>Get whether a string is a valid 'slug', containing only basic characters that are safe in all contexts (e.g. filenames, URLs, etc).</summary> /// <param name="str">The string to check.</param> public static bool IsSlug(string str) |