From f812279955dad7b1222cb3b6b68040c0673052b9 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Fri, 19 Jan 2024 10:47:07 -0500 Subject: [PATCH 01/44] fix template issue --- .../Templates/Test/Actions/install-qlt.liquid | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/CodeQLToolkit.Features/Templates/Test/Actions/install-qlt.liquid b/src/CodeQLToolkit.Features/Templates/Test/Actions/install-qlt.liquid index 56e357d..cb53a5c 100644 --- a/src/CodeQLToolkit.Features/Templates/Test/Actions/install-qlt.liquid +++ b/src/CodeQLToolkit.Features/Templates/Test/Actions/install-qlt.liquid @@ -30,6 +30,8 @@ runs: ADD_TO_PATH: ${{ inputs.add-to-path }} QLT_VERSION: ${{ inputs.qlt-version }} QLT_HOME: ${{ inputs.qlt-home }} + GITHUB_TOKEN: ${{ github.token }} + shell: bash run: | echo -e "\e[0;32m[QLT]\e[0m Determining QLT release for $RUNNER_OS" From 26659f28aadb97ccba3af9b37b1b355e517d3e25 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Fri, 19 Jan 2024 18:50:15 -0500 Subject: [PATCH 02/44] code for enabling and disabling and checking on status of custom bundles --- example/qlt.conf.json | 4 +- src/CodeQLToolkit.Core/Main.cs | 3 + .../Properties/launchSettings.json | 2 +- .../Bundle/BundleFeatureMain.cs | 52 +++++++++ .../Bundle/Commands/BundleCommandFeature.cs | 68 ++++++++++++ .../Bundle/Lifecycle/BaseLifecycleTarget.cs | 19 ++++ .../Lifecycle/BundleLifecycleFeature.cs | 100 ++++++++++++++++++ ...abledCustomCodeQLBundlesLifecycleTarget.cs | 31 ++++++ ...sableCustomCodeQLBundlesLifecycleTarget.cs | 32 ++++++ ...nableCustomCodeQLBundlesLifecycleTarget.cs | 32 ++++++ .../CodeQLToolkit.Features.csproj | 3 + src/CodeQLToolkit.Shared/Utils/QLTConfig.cs | 14 ++- 12 files changed, 356 insertions(+), 4 deletions(-) create mode 100644 src/CodeQLToolkit.Features/Bundle/BundleFeatureMain.cs create mode 100644 src/CodeQLToolkit.Features/Bundle/Commands/BundleCommandFeature.cs create mode 100644 src/CodeQLToolkit.Features/Bundle/Lifecycle/BaseLifecycleTarget.cs create mode 100644 src/CodeQLToolkit.Features/Bundle/Lifecycle/BundleLifecycleFeature.cs create mode 100644 src/CodeQLToolkit.Features/Bundle/Lifecycle/Targets/GetEnabledCustomCodeQLBundlesLifecycleTarget.cs create mode 100644 src/CodeQLToolkit.Features/Bundle/Lifecycle/Targets/SetDisableCustomCodeQLBundlesLifecycleTarget.cs create mode 100644 src/CodeQLToolkit.Features/Bundle/Lifecycle/Targets/SetEnableCustomCodeQLBundlesLifecycleTarget.cs diff --git a/example/qlt.conf.json b/example/qlt.conf.json index a13745c..eb74899 100644 --- a/example/qlt.conf.json +++ b/example/qlt.conf.json @@ -1,5 +1,7 @@ { "CodeQLCLI": "2.11.6", "CodeQLStandardLibrary": "codeql-cli/v2.11.6", - "CodeQLCLIBundle": "codeql-bundle-20221211" + "CodeQLCLIBundle": "codeql-bundle-20221211", + "EnableCustomCodeQLBundles": false, + "CodeQLStandardLibraryIdent": "codeql-cli_v2.11.6" } \ No newline at end of file diff --git a/src/CodeQLToolkit.Core/Main.cs b/src/CodeQLToolkit.Core/Main.cs index fdb0f53..fda2c6a 100644 --- a/src/CodeQLToolkit.Core/Main.cs +++ b/src/CodeQLToolkit.Core/Main.cs @@ -10,6 +10,7 @@ using CodeQLToolkit.Features.Test; using CodeQLToolkit.Features.Pack; using CodeQLToolkit.Features.Validation; +using CodeQLToolkit.Features.Bundle; namespace CodeQLDevelopmentLifecycleToolkit.Core { @@ -49,6 +50,8 @@ public static async Task Main(string[] args) PackFeatureMain.Instance.Register(rootCommand); // Register the `Validation` feature ValidationFeatureMain.Instance.Register(rootCommand); + // Register the `Bundle` feature + BundleFeatureMain.Instance.Register(rootCommand); return await rootCommand.InvokeAsync(args); } diff --git a/src/CodeQLToolkit.Core/Properties/launchSettings.json b/src/CodeQLToolkit.Core/Properties/launchSettings.json index 9b17472..58a6cf3 100644 --- a/src/CodeQLToolkit.Core/Properties/launchSettings.json +++ b/src/CodeQLToolkit.Core/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "CodeQLToolkit.Core": { "commandName": "Project", - "commandLineArgs": " --base C:\\QLPACKTEST test init" + "commandLineArgs": "--base C:\\Projects\\codeql-development-lifecycle-toolkit\\example bundle set enable-custom-bundles" } } } \ No newline at end of file diff --git a/src/CodeQLToolkit.Features/Bundle/BundleFeatureMain.cs b/src/CodeQLToolkit.Features/Bundle/BundleFeatureMain.cs new file mode 100644 index 0000000..d55f223 --- /dev/null +++ b/src/CodeQLToolkit.Features/Bundle/BundleFeatureMain.cs @@ -0,0 +1,52 @@ +using CodeQLToolkit.Features.Test.Commands; +using CodeQLToolkit.Features.Validation.Lifecycle; +using CodeQLToolkit.Features.Validation; +using System; +using System.Collections.Generic; +using System.CommandLine; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using CodeQLToolkit.Features.Bundle.Commands; +using CodeQLToolkit.Features.Bundle.Lifecycle; + +namespace CodeQLToolkit.Features.Bundle +{ + public class BundleFeatureMain : IToolkitFeature + + { + readonly BundleCommandFeature commandFeature; + readonly BundleLifecycleFeature lifecycleFeature; + readonly static BundleFeatureMain instance; + + static BundleFeatureMain() + { + instance = new BundleFeatureMain(); + } + + private BundleFeatureMain() + { + commandFeature = new BundleCommandFeature(); + lifecycleFeature = new BundleLifecycleFeature(); + } + public static BundleFeatureMain Instance { get { return instance; } } + + public void Register(Command parentCommand) + { + var bundleFeatureCommand = new Command("bundle", "Features related creation and usage of custom CodeQL bundles."); + parentCommand.Add(bundleFeatureCommand); + + Log.G().LogInformation("Registering command submodule."); + commandFeature.Register(bundleFeatureCommand); + + Log.G().LogInformation("Registering lifecycle submodule."); + lifecycleFeature.Register(bundleFeatureCommand); + + } + + public int Run() + { + return 0; + } + } +} diff --git a/src/CodeQLToolkit.Features/Bundle/Commands/BundleCommandFeature.cs b/src/CodeQLToolkit.Features/Bundle/Commands/BundleCommandFeature.cs new file mode 100644 index 0000000..e8b12ea --- /dev/null +++ b/src/CodeQLToolkit.Features/Bundle/Commands/BundleCommandFeature.cs @@ -0,0 +1,68 @@ +using CodeQLToolkit.Shared.Utils; +using System; +using System.Collections.Generic; +using System.CommandLine; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CodeQLToolkit.Features.Bundle.Commands +{ + public class BundleCommandFeature : FeatureBase, IToolkitLifecycleFeature + { + public override LanguageType[] SupportedLangauges + { + get => new LanguageType[] { + LanguageType.C, + LanguageType.CPP, + LanguageType.CSHARP, + LanguageType.JAVA, + LanguageType.JAVASCRIPT, + LanguageType.GO, + LanguageType.RUBY, + LanguageType.PYTHON + }; + } + + public BundleCommandFeature() + { + FeatureName = "Bundle"; + } + + public void Register(Command parentCommand) + { + Log.G().LogInformation("Registering command submodule."); + + + var runCommand = new Command("run", "Functions pertaining running bundle commands."); + parentCommand.Add(runCommand); + + //var checkQueryQueriesCommand = new Command("check-queries", "Checks the query metadata for the specified language."); + + //var languageOption = new Option("--language", $"The language to run tests for.") { IsRequired = true }.FromAmong(SupportedLangauges.Select(x => x.ToOptionString()).ToArray()); + + //checkQueryQueriesCommand.Add(languageOption); + + //runCommand.Add(checkQueryQueriesCommand); + + + //checkQueryQueriesCommand.SetHandler((language, basePath, prettyPrint) => + //{ + // Log.G().LogInformation("Executing check-query-metadata command..."); + + // new CheckQueriesCommandTarget() + // { + // Base = basePath, + // Language = language, + // PrettyPrint = prettyPrint, + // }.Run(); + + //}, languageOption, Globals.BasePathOption, prettyPrintOption); + } + + public int Run() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/CodeQLToolkit.Features/Bundle/Lifecycle/BaseLifecycleTarget.cs b/src/CodeQLToolkit.Features/Bundle/Lifecycle/BaseLifecycleTarget.cs new file mode 100644 index 0000000..04b4b17 --- /dev/null +++ b/src/CodeQLToolkit.Features/Bundle/Lifecycle/BaseLifecycleTarget.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CodeQLToolkit.Features.Bundle.Lifecycle +{ + abstract public class BaseLifecycleTarget : ILifecycleTarget + { + public int NumThreads { get; set; } + public string UseRunner { get; set; } + + public string ExtraArgs { get; set; } + + + + } +} diff --git a/src/CodeQLToolkit.Features/Bundle/Lifecycle/BundleLifecycleFeature.cs b/src/CodeQLToolkit.Features/Bundle/Lifecycle/BundleLifecycleFeature.cs new file mode 100644 index 0000000..27611d2 --- /dev/null +++ b/src/CodeQLToolkit.Features/Bundle/Lifecycle/BundleLifecycleFeature.cs @@ -0,0 +1,100 @@ +using CodeQLToolkit.Features.CodeQL.Lifecycle.Targets; +using CodeQLToolkit.Features.CodeQL.Lifecycle; +using CodeQLToolkit.Features.Test.Lifecycle.Targets; +using CodeQLToolkit.Features.Test.Lifecycle.Targets.Actions; +using CodeQLToolkit.Shared.Utils; +using System.CommandLine; +using System.Reflection; +using CodeQLToolkit.Features.Bundle.Lifecycle.Targets; + +namespace CodeQLToolkit.Features.Bundle.Lifecycle +{ + public class BundleLifecycleFeature : FeatureBase, IToolkitLifecycleFeature + { + public BundleLifecycleFeature() + { + FeatureName = "Bundle"; + } + + public override LanguageType[] SupportedLangauges + { + get => new LanguageType[] { + LanguageType.C, + LanguageType.CPP, + LanguageType.CSHARP, + LanguageType.JAVA, + LanguageType.JAVASCRIPT, + LanguageType.GO, + LanguageType.RUBY, + LanguageType.PYTHON + }; + } + + public void Register(Command parentCommand) + { + Log.G().LogInformation("Registering lifecycle submodule."); + + var setCommand = new Command("set", "Functions pertaining to setting variables related to custom CodeQL bundles."); + parentCommand.Add(setCommand); + + var enableCommand = new Command("enable-custom-bundles", "Enables custom CodeQL Bundles."); + setCommand.Add(enableCommand); + + var disableCommand = new Command("disable-custom-bundles", "Disables custom CodeQL Bundles."); + setCommand.Add(disableCommand); + + var getCommand = new Command("get", "Functions pertaining to getting variables related to CodeQL Bundles."); + parentCommand.Add(getCommand); + + var getEnabledCommand = new Command("enabled", "Determines if custom CodeQL Bundles are enabled."); + getCommand.Add(getEnabledCommand); + + { + enableCommand.SetHandler((basePath) => + { + Log.G().LogInformation("Executing enable command..."); + + new SetEnableCustomCodeQLBundlesLifecycleTarget() + { + Base = basePath + }.Run(); + + }, Globals.BasePathOption); + } + + { + disableCommand.SetHandler((basePath) => + { + Log.G().LogInformation("Executing get enabled command..."); + + new SetDisableCustomCodeQLBundlesLifecycleTarget() + { + Base = basePath + }.Run(); + + }, Globals.BasePathOption); + } + + + { + getEnabledCommand.SetHandler((basePath) => + { + Log.G().LogInformation("Executing disable command..."); + + new GetEnabledCustomCodeQLBundlesLifecycleTarget() + { + Base = basePath + }.Run(); + + }, Globals.BasePathOption); + } + + + } + + public int Run() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/CodeQLToolkit.Features/Bundle/Lifecycle/Targets/GetEnabledCustomCodeQLBundlesLifecycleTarget.cs b/src/CodeQLToolkit.Features/Bundle/Lifecycle/Targets/GetEnabledCustomCodeQLBundlesLifecycleTarget.cs new file mode 100644 index 0000000..9e2e4d6 --- /dev/null +++ b/src/CodeQLToolkit.Features/Bundle/Lifecycle/Targets/GetEnabledCustomCodeQLBundlesLifecycleTarget.cs @@ -0,0 +1,31 @@ +using CodeQLToolkit.Shared.Utils; +using System; +using System.Collections.Generic; +using System.CommandLine; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CodeQLToolkit.Features.Bundle.Lifecycle.Targets +{ + public class GetEnabledCustomCodeQLBundlesLifecycleTarget : ILifecycleTarget + { + override public void Run() + { + Log.G().LogInformation("Running get enabled command..."); + + var c = new QLTConfig() + { + Base = Base + }; + + var config = c.FromFile(); + + Console.WriteLine($"---------current settings---------"); + Console.WriteLine($"CodeQL Custom Bundles Enabled: {config.EnableCustomCodeQLBundles}"); + Console.WriteLine($"----------------------------------"); + Console.WriteLine("(hint: use `qlt bundle set` to modify these values.)"); + + } + } +} diff --git a/src/CodeQLToolkit.Features/Bundle/Lifecycle/Targets/SetDisableCustomCodeQLBundlesLifecycleTarget.cs b/src/CodeQLToolkit.Features/Bundle/Lifecycle/Targets/SetDisableCustomCodeQLBundlesLifecycleTarget.cs new file mode 100644 index 0000000..dee5072 --- /dev/null +++ b/src/CodeQLToolkit.Features/Bundle/Lifecycle/Targets/SetDisableCustomCodeQLBundlesLifecycleTarget.cs @@ -0,0 +1,32 @@ +using CodeQLToolkit.Shared.Utils; +using System; +using System.Collections.Generic; +using System.CommandLine; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CodeQLToolkit.Features.Bundle.Lifecycle.Targets +{ + public class SetDisableCustomCodeQLBundlesLifecycleTarget : ILifecycleTarget + { + override public void Run() + { + Log.G().LogInformation("Running set command..."); + + var c = new QLTConfig() + { + Base = Base + }; + + var config = c.FromFile(); + + config.EnableCustomCodeQLBundles = false; + + config.ToFile(); + + Log.G().LogInformation("Wrote to file..."); + + } + } +} diff --git a/src/CodeQLToolkit.Features/Bundle/Lifecycle/Targets/SetEnableCustomCodeQLBundlesLifecycleTarget.cs b/src/CodeQLToolkit.Features/Bundle/Lifecycle/Targets/SetEnableCustomCodeQLBundlesLifecycleTarget.cs new file mode 100644 index 0000000..a59a1c7 --- /dev/null +++ b/src/CodeQLToolkit.Features/Bundle/Lifecycle/Targets/SetEnableCustomCodeQLBundlesLifecycleTarget.cs @@ -0,0 +1,32 @@ +using CodeQLToolkit.Shared.Utils; +using System; +using System.Collections.Generic; +using System.CommandLine; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CodeQLToolkit.Features.Bundle.Lifecycle.Targets +{ + public class SetEnableCustomCodeQLBundlesLifecycleTarget : ILifecycleTarget + { + override public void Run() + { + Log.G().LogInformation("Running set command..."); + + var c = new QLTConfig() + { + Base = Base + }; + + var config = c.FromFile(); + + config.EnableCustomCodeQLBundles = true; + + config.ToFile(); + + Log.G().LogInformation("Wrote to file..."); + + } + } +} diff --git a/src/CodeQLToolkit.Features/CodeQLToolkit.Features.csproj b/src/CodeQLToolkit.Features/CodeQLToolkit.Features.csproj index 269a128..5ed572e 100644 --- a/src/CodeQLToolkit.Features/CodeQLToolkit.Features.csproj +++ b/src/CodeQLToolkit.Features/CodeQLToolkit.Features.csproj @@ -11,6 +11,9 @@ + + + diff --git a/src/CodeQLToolkit.Shared/Utils/QLTConfig.cs b/src/CodeQLToolkit.Shared/Utils/QLTConfig.cs index a209548..7c8a1a9 100644 --- a/src/CodeQLToolkit.Shared/Utils/QLTConfig.cs +++ b/src/CodeQLToolkit.Shared/Utils/QLTConfig.cs @@ -12,10 +12,16 @@ public class QLTConfig public string CodeQLCLI { get; set; } public string CodeQLStandardLibrary { get; set; } public string CodeQLCLIBundle { get; set; } + + public bool EnableCustomCodeQLBundles { get; set; } public string CodeQLStandardLibraryIdent { get { - return CodeQLStandardLibrary.Replace("/", "_"); + if (CodeQLStandardLibrary != null) + { + return CodeQLStandardLibrary.Replace("/", "_"); + } + return CodeQLStandardLibrary; } } @@ -34,7 +40,11 @@ public string CodeQLConfigFilePath public QLTConfig FromFile() { var data = File.ReadAllText(CodeQLConfigFilePath); - return JsonConvert.DeserializeObject(data); + QLTConfig c = JsonConvert.DeserializeObject(data); + + + c.Base = Base; + return c; } public void ToFile() From fe11c7974783becdec616a16215ccb3e342ce473 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Wed, 24 Jan 2024 14:37:06 -0500 Subject: [PATCH 03/44] work to enable codeql version mgmt --- .../CodeQL/CodeQLFeatureMain.cs | 8 ++- .../CodeQL/Commands/CodeQLCommandFeature.cs | 66 +++++++++++++++++++ .../CodeQL/Commands/Targets/InstallCommand.cs | 18 +++++ .../CodeQL/Commands/Targets/ListCommand.cs | 17 +++++ .../CodeQL/Commands/Targets/UseCommand.cs | 18 +++++ .../CodeQLToolkit.Features.csproj | 3 +- 6 files changed, 127 insertions(+), 3 deletions(-) create mode 100644 src/CodeQLToolkit.Features/CodeQL/Commands/CodeQLCommandFeature.cs create mode 100644 src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs create mode 100644 src/CodeQLToolkit.Features/CodeQL/Commands/Targets/ListCommand.cs create mode 100644 src/CodeQLToolkit.Features/CodeQL/Commands/Targets/UseCommand.cs diff --git a/src/CodeQLToolkit.Features/CodeQL/CodeQLFeatureMain.cs b/src/CodeQLToolkit.Features/CodeQL/CodeQLFeatureMain.cs index c0357b8..44d0e3f 100644 --- a/src/CodeQLToolkit.Features/CodeQL/CodeQLFeatureMain.cs +++ b/src/CodeQLToolkit.Features/CodeQL/CodeQLFeatureMain.cs @@ -3,12 +3,14 @@ using CodeQLToolkit.Shared.Logging; using Microsoft.Extensions.Logging; using CodeQLToolkit.Features.CodeQL.Lifecycle; +using CodeQLToolkit.Features.CodeQL.Commands; namespace CodeQLToolkit.Features.CodeQL { public class CodeQLFeatureMain : IToolkitFeature { readonly CodeQLLifecycleFeature lifecycleFeature; + readonly CodeQLCommandFeature commandFeature; readonly static CodeQLFeatureMain instance; static CodeQLFeatureMain() @@ -19,6 +21,7 @@ static CodeQLFeatureMain() private CodeQLFeatureMain() { lifecycleFeature = new CodeQLLifecycleFeature(); + commandFeature = new CodeQLCommandFeature(); } public static CodeQLFeatureMain Instance { get { return instance; } } @@ -35,8 +38,11 @@ public void Register(Command parentCommand) parentCommand.Add(queryCommand); Log.G().LogInformation("Registering scaffolding submodule."); lifecycleFeature.Register(queryCommand); - } + Log.G().LogInformation("Registering command submodule."); + commandFeature.Register(queryCommand); + + } } } \ No newline at end of file diff --git a/src/CodeQLToolkit.Features/CodeQL/Commands/CodeQLCommandFeature.cs b/src/CodeQLToolkit.Features/CodeQL/Commands/CodeQLCommandFeature.cs new file mode 100644 index 0000000..baace68 --- /dev/null +++ b/src/CodeQLToolkit.Features/CodeQL/Commands/CodeQLCommandFeature.cs @@ -0,0 +1,66 @@ +using CodeQLToolkit.Features.Test.Commands.Targets; +using CodeQLToolkit.Features.Test.Lifecycle; +using CodeQLToolkit.Shared.Types; +using CodeQLToolkit.Shared.Utils; +using Microsoft.VisualBasic; +using System; +using System.Collections.Generic; +using System.CommandLine; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CodeQLToolkit.Features.CodeQL.Commands +{ + public class CodeQLCommandFeature : FeatureBase, IToolkitLifecycleFeature + { + public override LanguageType[] SupportedLangauges { get => new LanguageType[] { + LanguageType.C, + LanguageType.CPP, + LanguageType.CSHARP, + LanguageType.JAVA, + LanguageType.JAVASCRIPT, + LanguageType.GO, + LanguageType.RUBY, + LanguageType.PYTHON + }; } + + public CodeQLCommandFeature() + { + FeatureName = "CodeQL"; + } + + public void Register(Command parentCommand) + { + Log.G().LogInformation("Registering command submodule."); + + var runCommand = new Command("run", "Functions pertaining to running codeql-related commands."); + parentCommand.Add(runCommand); + + var installCommand = new Command("install", "Installs CodeQL (bundle or release distribution) locally."); + var useCommand = new Command("use", "Switches tooling to use a different CodeQL version and updates the paths accordingly."); + var listCommand = new Command("list", "Lists versions of CodeQL available locally."); + + runCommand.Add(installCommand); + runCommand.Add(useCommand); + runCommand.Add(listCommand); + + + installCommand.SetHandler((basePath, automationType) => + { + Log.G().LogInformation("Executing validate-unit-tests command..."); + + new ValidateUnitTestsCommand() + { + }.Run(); + + + }, Globals.BasePathOption, Globals.AutomationTypeOption); + } + + public int Run() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs b/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs new file mode 100644 index 0000000..4fc693c --- /dev/null +++ b/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CodeQLToolkit.Features.CodeQL.Commands.Targets +{ + public class InstallCommand : CommandTarget + { + + public override void Run() + { + Log.G().LogInformation($"Running Install command"); + } + } +} diff --git a/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/ListCommand.cs b/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/ListCommand.cs new file mode 100644 index 0000000..e9cf658 --- /dev/null +++ b/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/ListCommand.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CodeQLToolkit.Features.CodeQL.Commands.Targets +{ + public class ListCommand : CommandTarget + { + public override void Run() + { + Log.G().LogInformation($"Running List Command"); + } + } +} diff --git a/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/UseCommand.cs b/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/UseCommand.cs new file mode 100644 index 0000000..31a37a1 --- /dev/null +++ b/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/UseCommand.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CodeQLToolkit.Features.CodeQL.Commands.Targets +{ + public class UseCommand : CommandTarget + { + public override void Run() + { + Log.G().LogInformation($"Running Use command"); + + } + } +} diff --git a/src/CodeQLToolkit.Features/CodeQLToolkit.Features.csproj b/src/CodeQLToolkit.Features/CodeQLToolkit.Features.csproj index 5ed572e..189e3a6 100644 --- a/src/CodeQLToolkit.Features/CodeQLToolkit.Features.csproj +++ b/src/CodeQLToolkit.Features/CodeQLToolkit.Features.csproj @@ -1,4 +1,4 @@ - + net6.0 @@ -14,7 +14,6 @@ - From b2c31987de6b0cff1901c57dd11010f41b415785 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Mon, 29 Jan 2024 18:16:58 -0500 Subject: [PATCH 04/44] installations working for standard codeql installations --- .../CodeQL/CodeQLFeatureMain.cs | 2 +- .../CodeQL/Commands/CodeQLCommandFeature.cs | 10 +- .../CodeQL/Commands/Targets/InstallCommand.cs | 25 +- .../CodeQLToolkit.Features.csproj | 4 + .../CodeQL/CodeQLCustomBundleInstallation.cs | 12 + .../CodeQL/CodeQLInstallation.cs | 270 ++++++++++++++++++ .../CodeQL/InstallationRepository.cs | 65 +++++ src/CodeQLToolkit.Shared/CodeQL/RESOLUTION.md | 33 +++ .../CodeQLToolkit.Shared.csproj | 1 + src/CodeQLToolkit.Shared/Utils/FileUtils.cs | 12 + src/CodeQLToolkit.Shared/Utils/StringUtils.cs | 23 ++ .../Utils/FileUtilsTest.cs | 24 ++ 12 files changed, 475 insertions(+), 6 deletions(-) create mode 100644 src/CodeQLToolkit.Shared/CodeQL/CodeQLCustomBundleInstallation.cs create mode 100644 src/CodeQLToolkit.Shared/CodeQL/CodeQLInstallation.cs create mode 100644 src/CodeQLToolkit.Shared/CodeQL/InstallationRepository.cs create mode 100644 src/CodeQLToolkit.Shared/CodeQL/RESOLUTION.md create mode 100644 src/CodeQLToolkit.Shared/Utils/StringUtils.cs diff --git a/src/CodeQLToolkit.Features/CodeQL/CodeQLFeatureMain.cs b/src/CodeQLToolkit.Features/CodeQL/CodeQLFeatureMain.cs index 44d0e3f..ee51b92 100644 --- a/src/CodeQLToolkit.Features/CodeQL/CodeQLFeatureMain.cs +++ b/src/CodeQLToolkit.Features/CodeQL/CodeQLFeatureMain.cs @@ -36,10 +36,10 @@ public void Register(Command parentCommand) { var queryCommand = new Command("codeql", "Use the features related to managing the version of CodeQL used by this repository."); parentCommand.Add(queryCommand); + Log.G().LogInformation("Registering scaffolding submodule."); lifecycleFeature.Register(queryCommand); - Log.G().LogInformation("Registering command submodule."); commandFeature.Register(queryCommand); diff --git a/src/CodeQLToolkit.Features/CodeQL/Commands/CodeQLCommandFeature.cs b/src/CodeQLToolkit.Features/CodeQL/Commands/CodeQLCommandFeature.cs index baace68..6523dff 100644 --- a/src/CodeQLToolkit.Features/CodeQL/Commands/CodeQLCommandFeature.cs +++ b/src/CodeQLToolkit.Features/CodeQL/Commands/CodeQLCommandFeature.cs @@ -1,4 +1,5 @@ -using CodeQLToolkit.Features.Test.Commands.Targets; +using CodeQLToolkit.Features.CodeQL.Commands.Targets; +using CodeQLToolkit.Features.Test.Commands.Targets; using CodeQLToolkit.Features.Test.Lifecycle; using CodeQLToolkit.Shared.Types; using CodeQLToolkit.Shared.Utils; @@ -43,15 +44,16 @@ public void Register(Command parentCommand) runCommand.Add(installCommand); runCommand.Add(useCommand); - runCommand.Add(listCommand); + //runCommand.Add(listCommand); installCommand.SetHandler((basePath, automationType) => { - Log.G().LogInformation("Executing validate-unit-tests command..."); + Log.G().LogInformation("Executing install command..."); - new ValidateUnitTestsCommand() + new InstallCommand() { + Base = basePath, }.Run(); diff --git a/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs b/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs index 4fc693c..eabdf61 100644 --- a/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs +++ b/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs @@ -1,4 +1,5 @@ -using Newtonsoft.Json; +using CodeQLToolkit.Shared.CodeQL; +using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; @@ -13,6 +14,28 @@ public class InstallCommand : CommandTarget public override void Run() { Log.G().LogInformation($"Running Install command"); + + // First, check if CodeQL is installed. + var installation = CodeQLInstallation.LoadFromConfig(Base); + + Log.G().LogInformation($"Checking for installation..."); + + if (installation.IsInstalled()) + { + Log.G().LogInformation($"CodeQL is already installed at that version. Please delete the installation directory to reinstall."); + } + else + { + Log.G().LogInformation($"Installing CodeQL..."); + installation.Install(); + } + + + Log.G().LogInformation($"Done."); + + + + } } } diff --git a/src/CodeQLToolkit.Features/CodeQLToolkit.Features.csproj b/src/CodeQLToolkit.Features/CodeQLToolkit.Features.csproj index 189e3a6..efc6fed 100644 --- a/src/CodeQLToolkit.Features/CodeQLToolkit.Features.csproj +++ b/src/CodeQLToolkit.Features/CodeQLToolkit.Features.csproj @@ -18,6 +18,10 @@ + + + + Always diff --git a/src/CodeQLToolkit.Shared/CodeQL/CodeQLCustomBundleInstallation.cs b/src/CodeQLToolkit.Shared/CodeQL/CodeQLCustomBundleInstallation.cs new file mode 100644 index 0000000..b7c956a --- /dev/null +++ b/src/CodeQLToolkit.Shared/CodeQL/CodeQLCustomBundleInstallation.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CodeQLToolkit.Shared.CodeQL +{ + public class CodeQLCustomBundleInstallation : CodeQLInstallation + { + } +} diff --git a/src/CodeQLToolkit.Shared/CodeQL/CodeQLInstallation.cs b/src/CodeQLToolkit.Shared/CodeQL/CodeQLInstallation.cs new file mode 100644 index 0000000..3c5dcf8 --- /dev/null +++ b/src/CodeQLToolkit.Shared/CodeQL/CodeQLInstallation.cs @@ -0,0 +1,270 @@ +using System.Net; +using CodeQLToolkit.Shared.Utils; +using System.Runtime.InteropServices; +using Microsoft.Extensions.Logging; +using CodeQLToolkit.Shared.Logging; +using LibGit2Sharp; +using System.IO.Compression; +using System.Runtime.CompilerServices; + + +namespace CodeQLToolkit.Shared.CodeQL +{ + public class CodeQLInstallation + { + public string CLIVersion { get; set; } + public string StandardLibraryVersion { get; set; } + public string CLIBundle { get; set; } + public string StandardLibraryIdent { get; set; } + public bool EnableCustomCodeQLBundles { get; set; } + public string Base { get; set; } + + public static CodeQLInstallation LoadFromConfig(string Base) + { + var c = new QLTConfig() + { + Base = Base + }; + + return LoadFromConfig(c.FromFile()); + } + + public static CodeQLInstallation LoadFromConfig(QLTConfig c) + { + var config = c.FromFile(); + + + return new CodeQLInstallation + { + EnableCustomCodeQLBundles = config.EnableCustomCodeQLBundles, + CLIVersion = config.CodeQLCLI, + CLIBundle = config.CodeQLCLIBundle, + StandardLibraryIdent = config.CodeQLStandardLibraryIdent, + StandardLibraryVersion = config.CodeQLStandardLibrary, + Base = config.Base + }; + + + } + + public ArtifactKind Kind + { + get + { + if (EnableCustomCodeQLBundles) + { + return ArtifactKind.CUSTOM_BUNDLE; + } + + // TODO - here we only support the kind + // we package together however this can be extended + // to support the precompiled packages. + + return ArtifactKind.PACKAGE; + } + } + + + + public string PlatformID + { + get + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return "win64"; + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return "linux64"; + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return "osx64"; + } + + throw new Exception("Unknown platform."); + } + } + + public string PlatformExtension + { + get + { + // for now they are all zips, + return "zip"; + } + } + + public void Install() + { + // each time we download the file; however, + // .qlt/repo/packages/ident + // .qlt/repo/custom-bundle/ident + // .qlt/repo/bundle/ident + + // https://github.com/github/codeql-cli-binaries/releases/download/v2.16.0/codeql-linux64.zip + + // workout a destination directory for this + + if(Kind == ArtifactKind.CUSTOM_BUNDLE) + { + CustomBundleInstall(); + }else if(Kind == ArtifactKind.BUNDLE) + { + BundleInstall(); + }else + { + PackageInstall(); + } + + + + + } + + private void CustomBundleInstall() + { + throw new NotImplementedException(); + } + + private void BundleInstall() + { + throw new NotImplementedException(); + } + + public string StdLibDirectory + { + get + { + return Path.Combine(InstallationDirectory, "codeql-stdlib"); + + } + } + + public string CodeQLDirectory + { + get + { + return Path.Combine(InstallationDirectory, "codeql"); + + } + } + + private void PackageInstall() + { + Log.G().LogInformation($"Begin Installation "); + Log.G().LogInformation($"Requested CLI Version {CLIVersion}, Standard Library Ident: {StandardLibraryIdent}"); + + Log.G().LogInformation($"Create installation directory {InstallationDirectory}"); + + Directory.CreateDirectory(InstallationDirectory); + + + Log.G().LogInformation($"Download CodeQL CLI..."); + + + var downloadFile = $"codeql-{PlatformID}.{PlatformExtension}"; + + // first, download the cli. + using (var client = new WebClient()) + { + string uri = $"https://github.com/github/codeql-cli-binaries/releases/download/v{CLIVersion}/{downloadFile}"; + Log.G().LogInformation($"Remote URL: {uri}..."); + + client.DownloadFile(uri, Path.Combine(InstallationDirectory, downloadFile)); + } + + // unpack + Log.G().LogInformation($"Unpacking distribution..."); + ZipFile.ExtractToDirectory(Path.Combine(InstallationDirectory, downloadFile), InstallationDirectory); + Log.G().LogInformation($"Done."); + + Log.G().LogInformation($"Checkout standard library into.. {StdLibDirectory}"); + + var repoPath = Repository.Clone("https://github.com/github/codeql.git", StdLibDirectory); + + + Log.G().LogInformation($"Getting standard library version.. {StandardLibraryVersion}"); + + using (var repo = new Repository(repoPath)) + { + var tag = repo.Tags[$"refs/tags/{StandardLibraryVersion}"]; + + if (tag == null) + { + Log.G().LogInformation($"Unknown standard library version: {StandardLibraryVersion}"); + throw new Exception($"Unknown standard library version: {StandardLibraryVersion}"); + } + + Branch b = Commands.Checkout(repo, $"refs/tags/{StandardLibraryVersion}"); + } + } + + + + private string GetIdentForPackage(ArtifactKind k) + { + if (k == ArtifactKind.PACKAGE) + { + var ident = String.Join("", "codeql-cli-" + CLIVersion, "#standard-library-ident-" ,StandardLibraryIdent); + return StringUtils.CreateMD5(FileUtils.SanitizeFilename(ident)).ToLower(); + } + + throw new NotImplementedException(); + } + + public bool IsInstalled() + { + Log.G().LogInformation($"Checking if codeql is installed..."); + Log.G().LogInformation($"Requested CLI Version {CLIVersion}, Standard Library Ident: {StandardLibraryIdent}"); + + // if custom bundles are enabled + // they are doing local development or running + // unit tests. In either case, we want to make sure + // to reassemble the bundle. + if (EnableCustomCodeQLBundles) + { + Log.G().LogInformation($"Custom bundle mode."); + return false; + } + + Log.G().LogInformation($"CodeQL Package Mode"); + Log.G().LogInformation($"Checking for existance of directory {InstallationDirectory}"); + + if (Directory.Exists(InstallationDirectory) && Directory.Exists(CodeQLDirectory) && Directory.Exists(StdLibDirectory)) + { + Log.G().LogInformation($"CodeQL Installation exists. CLI Version {CLIVersion}, Standard Library Ident: {StandardLibraryIdent}"); + return true; + } + + return false; + } + + public string InstallationDirectory + { + get { + return GetInstallationDirectory(Kind); + } + } + + public string GetInstallationDirectory(ArtifactKind k) + { + return InstallationRepository.DirectoryForVersion(k, GetIdentForPackage(k)); + + } + + public string CodeQLHome { get { + return ""; + } + } + + public string CodeQLToolBinary { get { + return Path.Combine("", ""); + } + } + + } +} diff --git a/src/CodeQLToolkit.Shared/CodeQL/InstallationRepository.cs b/src/CodeQLToolkit.Shared/CodeQL/InstallationRepository.cs new file mode 100644 index 0000000..1c77a4f --- /dev/null +++ b/src/CodeQLToolkit.Shared/CodeQL/InstallationRepository.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CodeQLToolkit.Shared.CodeQL +{ + public enum ArtifactKind { BUNDLE, CUSTOM_BUNDLE, PACKAGE }; + + public class InstallationRepository + { + readonly static string PAKCAGE_DIRECTORY = "packages"; + readonly static string BUNDLE_DIRECTORY = "bundle"; + readonly static string CUSTOM_BUNDLE_DIRECTORY = "custom-bundle"; + + public static string GetLocation + { + get + { + return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".qlt"); + } + } + + public static string PackageLocation + { + get + { + return Path.Combine(GetLocation, PAKCAGE_DIRECTORY); + } + } + + public static string BundleLocation + { + get + { + return Path.Combine(GetLocation, BUNDLE_DIRECTORY); + } + } + + public static string CustomBundleLocation + { + get + { + return Path.Combine(GetLocation, CUSTOM_BUNDLE_DIRECTORY); + } + } + + + public static string DirectoryForVersion(ArtifactKind kind, string version) + { + if(kind == ArtifactKind.PACKAGE) + { + return Path.Combine(PackageLocation, version); + } + else if (kind == ArtifactKind.BUNDLE) { + return Path.Combine(BundleLocation, version); + } + else + { + return Path.Combine(CustomBundleLocation, version); + } + } + } +} diff --git a/src/CodeQLToolkit.Shared/CodeQL/RESOLUTION.md b/src/CodeQLToolkit.Shared/CodeQL/RESOLUTION.md new file mode 100644 index 0000000..3878f3d --- /dev/null +++ b/src/CodeQLToolkit.Shared/CodeQL/RESOLUTION.md @@ -0,0 +1,33 @@ +# How QLT Resolves and Uses Standard CodeQL installations and Custom bundles. + +The layout of the installation repository is as follows: + +``` +.qlt/repo/packages/ident <-- normal CodeQL installs +.qlt/repo/custom-bundle/ident <-- custom bundles +.qlt/repo/bundle/ident <-- precompiled CodeQL bundles which include the cli and library. +``` + +The general intention is that the tooling is used as follows: + +``` +qlt codeql install +``` + +If the `--use-quick-bundles` flag is specified, QLT will construct a custom bundle in the repository and then set the `QLT_CODEQL_PATH` +variable. This enables one to use this both in automation AND within +a local developer environment. + +Note that `QLT_CODEQL_PATH` being set overrides all resolution attempted by QLT. + +If `QLT_CODEQL_PATH` is *not* set, then QLT will attempt to resolve the required version of CodeQL by looking for the +`qlt.conf.json` variable `CodeQLStandardLibraryIdent`, which is automatically set by QLT when the version of CodeQL is set. + +Note that for custom bundles, the variable `EnableCustomCodeQLBundles` controls if custom bundles will be used. In general, +when using QLT a previously installed version of CodeQL may easily be detected in the repository by looking for the ident. +However, since there is no ident for a custom bundle, doing a `qlt codeql install` is always necessary prior to invoking the other +functions of QLT. + +For cases where one simply wishes to use CodeQL without a custom bundle (MAD extensions, queries, etc) then QLT will attempt to resolve +the correct version of CodeQL by using the `CodeQLStandardLibraryIdent`. If this cannot be resolved, QLT will notify the user that +one must first run `qlt codeql install` prior to proceeding. diff --git a/src/CodeQLToolkit.Shared/CodeQLToolkit.Shared.csproj b/src/CodeQLToolkit.Shared/CodeQLToolkit.Shared.csproj index 506c392..a6d8dc4 100644 --- a/src/CodeQLToolkit.Shared/CodeQLToolkit.Shared.csproj +++ b/src/CodeQLToolkit.Shared/CodeQLToolkit.Shared.csproj @@ -7,6 +7,7 @@ + diff --git a/src/CodeQLToolkit.Shared/Utils/FileUtils.cs b/src/CodeQLToolkit.Shared/Utils/FileUtils.cs index 4f6d013..e483be2 100644 --- a/src/CodeQLToolkit.Shared/Utils/FileUtils.cs +++ b/src/CodeQLToolkit.Shared/Utils/FileUtils.cs @@ -27,5 +27,17 @@ public static string CreateTempDirectory(string baseDir) return tempDirectory; } + public static string SanitizeFilename(string filename) + { + string f = filename; + + foreach(var c in Path.GetInvalidFileNameChars()) + { + f = f.Replace(c, '_'); + } + + return f; + } + } } diff --git a/src/CodeQLToolkit.Shared/Utils/StringUtils.cs b/src/CodeQLToolkit.Shared/Utils/StringUtils.cs new file mode 100644 index 0000000..d9336fc --- /dev/null +++ b/src/CodeQLToolkit.Shared/Utils/StringUtils.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CodeQLToolkit.Shared.Utils +{ + public class StringUtils + { + public static string CreateMD5(string input) + { + // Use input string to calculate MD5 hash + using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create()) + { + byte[] inputBytes = Encoding.ASCII.GetBytes(input); + byte[] hashBytes = md5.ComputeHash(inputBytes); + + return Convert.ToHexString(hashBytes); + } + } + } +} diff --git a/test/CodeQLToolkit.Shared.Tests/Utils/FileUtilsTest.cs b/test/CodeQLToolkit.Shared.Tests/Utils/FileUtilsTest.cs index 71d70c7..1ab01df 100644 --- a/test/CodeQLToolkit.Shared.Tests/Utils/FileUtilsTest.cs +++ b/test/CodeQLToolkit.Shared.Tests/Utils/FileUtilsTest.cs @@ -29,5 +29,29 @@ public void TestCreateTempDirectoryWithPath() Assert.IsTrue(dir.StartsWith(Path.GetTempPath())); } + + + [Test] + public void TestSanitizeFilename() + { + string[] paths = new string[]{ + "invalid:#!/\\/path", + "codeql/cli-1.1.2" + }; + + string[] expected = new string[]{ + "invalid_#!___path", + "codeql_cli-1.1.2" + }; + + for (int i= 0; i < paths.Length; i++) + { + Console.WriteLine(i + "Actual: " + FileUtils.SanitizeFilename(paths[i])); + Console.WriteLine(i + "Expected: " + expected[i]); + + Assert.IsTrue(FileUtils.SanitizeFilename(paths[i]) == expected[i]); + } + + } } } From f23ce7978ed129752e4d9103c145c45f160afa05 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Fri, 2 Feb 2024 15:56:56 -0500 Subject: [PATCH 05/44] work --- .gitattributes | 5 +---- .gitignore | 3 ++- developer_guide.md | 24 +++++++++++++++++++++--- tools/codeql_bundle.exe | 3 +++ 4 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 tools/codeql_bundle.exe diff --git a/.gitattributes b/.gitattributes index 1ff0c42..b55df30 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,7 +2,6 @@ # Set default behavior to automatically normalize line endings. ############################################################################### * text=auto - ############################################################################### # Set default behavior for command prompt diff. # @@ -11,7 +10,6 @@ # Note: This is only used by command line ############################################################################### #*.cs diff=csharp - ############################################################################### # Set the merge driver for project and solution files # @@ -34,7 +32,6 @@ #*.modelproj merge=binary #*.sqlproj merge=binary #*.wwaproj merge=binary - ############################################################################### # behavior for image files # @@ -43,7 +40,6 @@ #*.jpg binary #*.png binary #*.gif binary - ############################################################################### # diff behavior for common document formats # @@ -61,3 +57,4 @@ #*.PDF diff=astextplain #*.rtf diff=astextplain #*.RTF diff=astextplain +codeql_bundle.exe filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore index 9491a2f..3c0b66a 100644 --- a/.gitignore +++ b/.gitignore @@ -360,4 +360,5 @@ MigrationBackup/ .ionide/ # Fody - auto-generated XML schema -FodyWeavers.xsd \ No newline at end of file +FodyWeavers.xsd +/dist/ diff --git a/developer_guide.md b/developer_guide.md index d6436d4..18c690a 100644 --- a/developer_guide.md +++ b/developer_guide.md @@ -1,13 +1,31 @@ # Developer Guide -# Feature Details +## Building external tools -## +External tools must be placed in the `tools` directory in the final distribution. During local development you can build your own binaries, in addition to placing them in the correct location manually. Binaries must be placed in: -## Test Feature +``` +codeql-development-lifecycle-toolkit/src/CodeQLToolkit.Core/bin/Debug/net6.0/tools +``` + +Assuming you are running the `Debug` configuration locally. + +Note that we keep recent copies of tools (for local debugging purposes) in the `tools` directory in the root of this repo. These are made available just to make local development easier and should not be used for production or distribution purposes. + +**CodeQL Bundle** + +``` +./scripts/build_codeql_bundle_dist.ps1 -Version 0.2.0 -WorkDirectory dist -DestinationDirectory ./src/CodeQLToolkit.Core/bin/Debug/net6.0/tools +``` +# Feature Details + +## + + +## Test Feature # Mapping Targets to Different Automation Types diff --git a/tools/codeql_bundle.exe b/tools/codeql_bundle.exe new file mode 100644 index 0000000..46346f0 --- /dev/null +++ b/tools/codeql_bundle.exe @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2aa19b0e7cba79a6c6d7b2c08932bedd0db092e64aaa12156e65d09d8c5855f0 +size 10540747 From d1d845078ae56ee1612365b5337fcc57c870f3de Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Tue, 6 Feb 2024 16:20:01 -0500 Subject: [PATCH 06/44] integrated CodeQL command into tool. --- .../CodeQL/Commands/CodeQLCommandFeature.cs | 6 +++--- .../Actions/ExecuteUnitTestsCommandTarget.cs | 13 ++++++++++++- .../CodeQL/CodeQLInstallation.cs | 15 +++++++++++---- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/CodeQLToolkit.Features/CodeQL/Commands/CodeQLCommandFeature.cs b/src/CodeQLToolkit.Features/CodeQL/Commands/CodeQLCommandFeature.cs index 6523dff..3f43c41 100644 --- a/src/CodeQLToolkit.Features/CodeQL/Commands/CodeQLCommandFeature.cs +++ b/src/CodeQLToolkit.Features/CodeQL/Commands/CodeQLCommandFeature.cs @@ -39,11 +39,11 @@ public void Register(Command parentCommand) parentCommand.Add(runCommand); var installCommand = new Command("install", "Installs CodeQL (bundle or release distribution) locally."); - var useCommand = new Command("use", "Switches tooling to use a different CodeQL version and updates the paths accordingly."); - var listCommand = new Command("list", "Lists versions of CodeQL available locally."); + //var useCommand = new Command("use", "Switches tooling to use a different CodeQL version and updates the paths accordingly."); + //var listCommand = new Command("list", "Lists versions of CodeQL available locally."); runCommand.Add(installCommand); - runCommand.Add(useCommand); + //runCommand.Add(useCommand); //runCommand.Add(listCommand); diff --git a/src/CodeQLToolkit.Features/Test/Commands/Targets/Actions/ExecuteUnitTestsCommandTarget.cs b/src/CodeQLToolkit.Features/Test/Commands/Targets/Actions/ExecuteUnitTestsCommandTarget.cs index 230e217..282d396 100644 --- a/src/CodeQLToolkit.Features/Test/Commands/Targets/Actions/ExecuteUnitTestsCommandTarget.cs +++ b/src/CodeQLToolkit.Features/Test/Commands/Targets/Actions/ExecuteUnitTestsCommandTarget.cs @@ -1,4 +1,5 @@ using CodeQLToolkit.Features.Test.Lifecycle.Models; +using CodeQLToolkit.Shared.CodeQL; using CodeQLToolkit.Shared.Utils; using System; using System.Collections.Generic; @@ -70,9 +71,19 @@ public override void Run() Log.G().LogInformation($"Slice: {slice} of {NumThreads}"); Log.G().LogInformation($"Report File: {outFileReport}..."); + + // Get A Copy of the installation + var installation = CodeQLInstallation.LoadFromConfig(Base); + + if(!installation.IsInstalled()) + { + DieWithError($"Requested CodeQL Version ({CLIVersion}) Not Installed. Run `qlt codeql run install` before running this step."); + } + + using (Process process = new Process()) { - process.StartInfo.FileName = "codeql"; + process.StartInfo.FileName = installation.CodeQLToolBinary; process.StartInfo.WorkingDirectory = workingDirectory; process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardOutput = true; diff --git a/src/CodeQLToolkit.Shared/CodeQL/CodeQLInstallation.cs b/src/CodeQLToolkit.Shared/CodeQL/CodeQLInstallation.cs index 3c5dcf8..0260e68 100644 --- a/src/CodeQLToolkit.Shared/CodeQL/CodeQLInstallation.cs +++ b/src/CodeQLToolkit.Shared/CodeQL/CodeQLInstallation.cs @@ -98,6 +98,7 @@ public string PlatformExtension } } + public void Install() { // each time we download the file; however, @@ -256,13 +257,19 @@ public string GetInstallationDirectory(ArtifactKind k) } - public string CodeQLHome { get { - return ""; + public string CodeQLHome { + get { + return CodeQLDirectory; } } - public string CodeQLToolBinary { get { - return Path.Combine("", ""); + public string CodeQLToolBinary { + get { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return Path.Combine(CodeQLDirectory, "codeql.exe"); + } + return Path.Combine(CodeQLDirectory, "codeql"); } } From cbf3106eaa5672a40e6089f3b5fc7e4afc33b298 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Tue, 6 Feb 2024 16:44:36 -0500 Subject: [PATCH 07/44] testing installation of new codeql --- .github/workflows/run-codeql-unit-tests-cpp.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/run-codeql-unit-tests-cpp.yml b/.github/workflows/run-codeql-unit-tests-cpp.yml index b953576..876c8a2 100644 --- a/.github/workflows/run-codeql-unit-tests-cpp.yml +++ b/.github/workflows/run-codeql-unit-tests-cpp.yml @@ -59,11 +59,10 @@ jobs: - name: Install CodeQL id: install-codeql - uses: ./.github/actions/install-codeql - with: - codeql-cli-version: ${{ matrix.codeql_cli }} - codeql-stdlib-version: ${{ matrix.codeql_standard_library }} - add-to-path: true + shell: bash + run: | + echo "Installing CodeQL" + qlt codeql run install --base example/ - name: Verify Versions of Tooling shell: bash From b057a957b693ff391e8376aa6938af590325babe Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Tue, 6 Feb 2024 16:58:12 -0500 Subject: [PATCH 08/44] export paths --- .github/workflows/run-codeql-unit-tests-cpp.yml | 5 +++-- .../CodeQL/Commands/Targets/InstallCommand.cs | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-codeql-unit-tests-cpp.yml b/.github/workflows/run-codeql-unit-tests-cpp.yml index 876c8a2..9c51c1e 100644 --- a/.github/workflows/run-codeql-unit-tests-cpp.yml +++ b/.github/workflows/run-codeql-unit-tests-cpp.yml @@ -67,9 +67,10 @@ jobs: - name: Verify Versions of Tooling shell: bash run: | - echo "CodeQL Home: ${{ steps.install-codeql.outputs.codeql-home }}" + echo "CodeQL Home: $QLT_CODEQL_HOME" + echo "CodeQL Binary: $QLT_CODEQL_PATH" echo -e "Checking CodeQL Version:" - codeql --version + $QLT_CODEQL_PATH --version echo -e "Checking QLT Version:" echo "QLT Home: ${{ steps.install-qlt.outputs.qlt-home }}" diff --git a/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs b/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs index eabdf61..719fa04 100644 --- a/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs +++ b/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs @@ -28,6 +28,11 @@ public override void Run() { Log.G().LogInformation($"Installing CodeQL..."); installation.Install(); + + // set the environment variable + Environment.SetEnvironmentVariable("QLT_CODEQL_HOME", installation.CodeQLHome); + Environment.SetEnvironmentVariable("QLT_CODEQL_PATH", installation.CodeQLToolBinary); + } From 9dfc9e7bf293bdb58536714af99c5c9c74e56eea Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Thu, 8 Feb 2024 17:04:06 -0500 Subject: [PATCH 09/44] env vars --- .github/workflows/run-codeql-unit-tests-cpp.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/run-codeql-unit-tests-cpp.yml b/.github/workflows/run-codeql-unit-tests-cpp.yml index 9c51c1e..1e6ae1b 100644 --- a/.github/workflows/run-codeql-unit-tests-cpp.yml +++ b/.github/workflows/run-codeql-unit-tests-cpp.yml @@ -63,6 +63,8 @@ jobs: run: | echo "Installing CodeQL" qlt codeql run install --base example/ + echo "QLT_CODEQL_HOME=$QLT_CODEQL_HOME" >> $GITHUB_ENV + echo "QLT_CODEQL_PATH=$QLT_CODEQL_PATH" >> $GITHUB_ENV - name: Verify Versions of Tooling shell: bash From 005c0a349674743aed024b5b70ef46fa831521c4 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Thu, 8 Feb 2024 17:08:04 -0500 Subject: [PATCH 10/44] checking paths --- .github/workflows/run-codeql-unit-tests-cpp.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/run-codeql-unit-tests-cpp.yml b/.github/workflows/run-codeql-unit-tests-cpp.yml index 1e6ae1b..8c66047 100644 --- a/.github/workflows/run-codeql-unit-tests-cpp.yml +++ b/.github/workflows/run-codeql-unit-tests-cpp.yml @@ -65,6 +65,10 @@ jobs: qlt codeql run install --base example/ echo "QLT_CODEQL_HOME=$QLT_CODEQL_HOME" >> $GITHUB_ENV echo "QLT_CODEQL_PATH=$QLT_CODEQL_PATH" >> $GITHUB_ENV + + echo "-----------------------------" + echo "CodeQL Home: $QLT_CODEQL_HOME" + echo "CodeQL Binary: $QLT_CODEQL_PATH" - name: Verify Versions of Tooling shell: bash From ca1c77332309c509e182915bb4a4d0bd47809e0b Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Thu, 8 Feb 2024 17:13:45 -0500 Subject: [PATCH 11/44] logging --- .../CodeQL/Commands/Targets/InstallCommand.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs b/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs index 719fa04..6f7cf4e 100644 --- a/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs +++ b/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs @@ -30,6 +30,9 @@ public override void Run() installation.Install(); // set the environment variable + Log.G().LogInformation($"Setting QLT_CODEQL_HOME to {installation.CodeQLHome}..."); + Log.G().LogInformation($"Setting QLT_CODEQL_PATH to {installation.CodeQLToolBinary}..."); + Environment.SetEnvironmentVariable("QLT_CODEQL_HOME", installation.CodeQLHome); Environment.SetEnvironmentVariable("QLT_CODEQL_PATH", installation.CodeQLToolBinary); From c28d198abcccb2d7efd9f78310b3b37962e10d45 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Thu, 8 Feb 2024 17:18:54 -0500 Subject: [PATCH 12/44] retarget --- .../CodeQL/Commands/Targets/InstallCommand.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs b/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs index 6f7cf4e..f274fc9 100644 --- a/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs +++ b/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs @@ -29,6 +29,7 @@ public override void Run() Log.G().LogInformation($"Installing CodeQL..."); installation.Install(); + // set the environment variable Log.G().LogInformation($"Setting QLT_CODEQL_HOME to {installation.CodeQLHome}..."); Log.G().LogInformation($"Setting QLT_CODEQL_PATH to {installation.CodeQLToolBinary}..."); From 9029e08cef59807004c5e25e7cf0e04b47ef9250 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Thu, 8 Feb 2024 17:20:06 -0500 Subject: [PATCH 13/44] retarget --- .github/workflows/run-codeql-unit-tests-cpp.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/run-codeql-unit-tests-cpp.yml b/.github/workflows/run-codeql-unit-tests-cpp.yml index 8c66047..a0371b1 100644 --- a/.github/workflows/run-codeql-unit-tests-cpp.yml +++ b/.github/workflows/run-codeql-unit-tests-cpp.yml @@ -5,9 +5,11 @@ on: push: branches: - 'main' + - 'jsinglet/**' pull_request: branches: - 'main' + - 'jsinglet/**' workflow_dispatch: From 96411057b83bd691f58b8462dbb70c32d0625863 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Thu, 8 Feb 2024 17:35:28 -0500 Subject: [PATCH 14/44] explicit writing to env --- .github/workflows/run-codeql-unit-tests-cpp.yml | 3 --- .../CodeQL/Commands/CodeQLCommandFeature.cs | 1 + .../CodeQL/Commands/Targets/InstallCommand.cs | 7 +++++++ src/CodeQLToolkit.Shared/Target/CommandTarget.cs | 1 + 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run-codeql-unit-tests-cpp.yml b/.github/workflows/run-codeql-unit-tests-cpp.yml index a0371b1..98d45f5 100644 --- a/.github/workflows/run-codeql-unit-tests-cpp.yml +++ b/.github/workflows/run-codeql-unit-tests-cpp.yml @@ -65,9 +65,6 @@ jobs: run: | echo "Installing CodeQL" qlt codeql run install --base example/ - echo "QLT_CODEQL_HOME=$QLT_CODEQL_HOME" >> $GITHUB_ENV - echo "QLT_CODEQL_PATH=$QLT_CODEQL_PATH" >> $GITHUB_ENV - echo "-----------------------------" echo "CodeQL Home: $QLT_CODEQL_HOME" echo "CodeQL Binary: $QLT_CODEQL_PATH" diff --git a/src/CodeQLToolkit.Features/CodeQL/Commands/CodeQLCommandFeature.cs b/src/CodeQLToolkit.Features/CodeQL/Commands/CodeQLCommandFeature.cs index 3f43c41..acbf71f 100644 --- a/src/CodeQLToolkit.Features/CodeQL/Commands/CodeQLCommandFeature.cs +++ b/src/CodeQLToolkit.Features/CodeQL/Commands/CodeQLCommandFeature.cs @@ -54,6 +54,7 @@ public void Register(Command parentCommand) new InstallCommand() { Base = basePath, + AutomationTarget = automationType, }.Run(); diff --git a/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs b/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs index f274fc9..32c5e45 100644 --- a/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs +++ b/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs @@ -1,4 +1,5 @@ using CodeQLToolkit.Shared.CodeQL; +using CodeQLToolkit.Shared.Types; using Newtonsoft.Json; using System; using System.Collections.Generic; @@ -37,6 +38,12 @@ public override void Run() Environment.SetEnvironmentVariable("QLT_CODEQL_HOME", installation.CodeQLHome); Environment.SetEnvironmentVariable("QLT_CODEQL_PATH", installation.CodeQLToolBinary); + if (AutomationTypeHelper.AutomationTypeFromString(AutomationTarget) == AutomationType.ACTIONS) + { + File.AppendAllText(Environment.GetEnvironmentVariable("GITHUB_ENV"), $"QLT_CODEQL_HOME={installation.CodeQLHome}"); + File.AppendAllText(Environment.GetEnvironmentVariable("GITHUB_ENV"), $"QLT_CODEQL_PATH={installation.CodeQLToolBinary}"); + } + } diff --git a/src/CodeQLToolkit.Shared/Target/CommandTarget.cs b/src/CodeQLToolkit.Shared/Target/CommandTarget.cs index a1338df..790675a 100644 --- a/src/CodeQLToolkit.Shared/Target/CommandTarget.cs +++ b/src/CodeQLToolkit.Shared/Target/CommandTarget.cs @@ -9,5 +9,6 @@ namespace CodeQLToolkit.Shared.Target public abstract class CommandTarget : ITarget { public string Language { get; set; } + public string AutomationTarget { get; set; } } } From b91243c0f71c4abfa0c8ee861fc7c710aa9a7630 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Thu, 8 Feb 2024 17:39:56 -0500 Subject: [PATCH 15/44] newines --- .../CodeQL/Commands/Targets/InstallCommand.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs b/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs index 32c5e45..e2e034c 100644 --- a/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs +++ b/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs @@ -40,8 +40,8 @@ public override void Run() if (AutomationTypeHelper.AutomationTypeFromString(AutomationTarget) == AutomationType.ACTIONS) { - File.AppendAllText(Environment.GetEnvironmentVariable("GITHUB_ENV"), $"QLT_CODEQL_HOME={installation.CodeQLHome}"); - File.AppendAllText(Environment.GetEnvironmentVariable("GITHUB_ENV"), $"QLT_CODEQL_PATH={installation.CodeQLToolBinary}"); + File.AppendAllText(Environment.GetEnvironmentVariable("GITHUB_ENV"), $"QLT_CODEQL_HOME={installation.CodeQLHome}" + "\n"); + File.AppendAllText(Environment.GetEnvironmentVariable("GITHUB_ENV"), $"QLT_CODEQL_PATH={installation.CodeQLToolBinary}" + "\n"); } } From 595350aaf715bcca3b7e35a9f5d017beca380342 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Thu, 8 Feb 2024 17:46:30 -0500 Subject: [PATCH 16/44] packs --- .../Commands/Targets/InstallQueryPacksCommandTarget.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/CodeQLToolkit.Features/Query/Commands/Targets/InstallQueryPacksCommandTarget.cs b/src/CodeQLToolkit.Features/Query/Commands/Targets/InstallQueryPacksCommandTarget.cs index 255faf7..a7bfcd0 100644 --- a/src/CodeQLToolkit.Features/Query/Commands/Targets/InstallQueryPacksCommandTarget.cs +++ b/src/CodeQLToolkit.Features/Query/Commands/Targets/InstallQueryPacksCommandTarget.cs @@ -1,4 +1,5 @@ -using System; +using CodeQLToolkit.Shared.CodeQL; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -18,13 +19,17 @@ public override void Run() Log.G().LogInformation($"Got {files.Length} packs..."); + + var installation = CodeQLInstallation.LoadFromConfig(Base); + + foreach ( string file in files ) { Log.G().LogInformation($"Installing qlpack {file}..."); using(Process process = new Process()) { - process.StartInfo.FileName = "codeql"; + process.StartInfo.FileName = installation.CodeQLToolBinary; process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardOutput = false; process.StartInfo.Arguments = $"pack install {file}"; From cc4cf7aae7d92a415873e3c60d75bc77cbade93e Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Fri, 9 Feb 2024 13:30:30 -0500 Subject: [PATCH 17/44] CodeQL should no longer have a seperate action --- .github/workflows/run-codeql-unit-tests-cpp.yml | 4 ---- .../Test/Lifecycle/Targets/Actions/InitLifecycleTarget.cs | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/run-codeql-unit-tests-cpp.yml b/.github/workflows/run-codeql-unit-tests-cpp.yml index 98d45f5..12e3e65 100644 --- a/.github/workflows/run-codeql-unit-tests-cpp.yml +++ b/.github/workflows/run-codeql-unit-tests-cpp.yml @@ -5,11 +5,9 @@ on: push: branches: - 'main' - - 'jsinglet/**' pull_request: branches: - 'main' - - 'jsinglet/**' workflow_dispatch: @@ -72,8 +70,6 @@ jobs: - name: Verify Versions of Tooling shell: bash run: | - echo "CodeQL Home: $QLT_CODEQL_HOME" - echo "CodeQL Binary: $QLT_CODEQL_PATH" echo -e "Checking CodeQL Version:" $QLT_CODEQL_PATH --version diff --git a/src/CodeQLToolkit.Features/Test/Lifecycle/Targets/Actions/InitLifecycleTarget.cs b/src/CodeQLToolkit.Features/Test/Lifecycle/Targets/Actions/InitLifecycleTarget.cs index b4b4dfd..e430196 100644 --- a/src/CodeQLToolkit.Features/Test/Lifecycle/Targets/Actions/InitLifecycleTarget.cs +++ b/src/CodeQLToolkit.Features/Test/Lifecycle/Targets/Actions/InitLifecycleTarget.cs @@ -28,7 +28,7 @@ public override void Run() // codeqlArgs = $"{codeqlArgs} {ExtraArgs}"; //} - WriteTemplateIfOverwriteOrNotExists("install-codeql", Path.Combine(Base, ".github", "actions", "install-codeql", "action.yml"), "install-codeql action"); + //WriteTemplateIfOverwriteOrNotExists("install-codeql", Path.Combine(Base, ".github", "actions", "install-codeql", "action.yml"), "install-codeql action"); WriteTemplateIfOverwriteOrNotExists("install-qlt", Path.Combine(Base, ".github", "actions", "install-qlt", "action.yml"), "install-qlt action"); WriteTemplateIfOverwriteOrNotExists("run-unit-tests", Path.Combine(Base, ".github", "workflows", $"run-codeql-unit-tests-{tmpLanguage}.yml"), $"Run CodeQL Unit Tests ({Language})", new { From d6ac6f65a4f53905d26aca57727e8cddcbb5d881 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Fri, 9 Feb 2024 15:06:19 -0500 Subject: [PATCH 18/44] update workflow --- .github/actions/install-qlt/action.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/actions/install-qlt/action.yml b/.github/actions/install-qlt/action.yml index 56e357d..cb53a5c 100644 --- a/.github/actions/install-qlt/action.yml +++ b/.github/actions/install-qlt/action.yml @@ -30,6 +30,8 @@ runs: ADD_TO_PATH: ${{ inputs.add-to-path }} QLT_VERSION: ${{ inputs.qlt-version }} QLT_HOME: ${{ inputs.qlt-home }} + GITHUB_TOKEN: ${{ github.token }} + shell: bash run: | echo -e "\e[0;32m[QLT]\e[0m Determining QLT release for $RUNNER_OS" From d51d472f9191ba899b429ef7c263cae1d1be82dd Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Fri, 9 Feb 2024 15:15:04 -0500 Subject: [PATCH 19/44] spacing --- .github/workflows/run-codeql-unit-tests-cpp.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/run-codeql-unit-tests-cpp.yml b/.github/workflows/run-codeql-unit-tests-cpp.yml index 12e3e65..77627a6 100644 --- a/.github/workflows/run-codeql-unit-tests-cpp.yml +++ b/.github/workflows/run-codeql-unit-tests-cpp.yml @@ -71,8 +71,7 @@ jobs: shell: bash run: | echo -e "Checking CodeQL Version:" - $QLT_CODEQL_PATH --version - + $QLT_CODEQL_PATH --version echo -e "Checking QLT Version:" echo "QLT Home: ${{ steps.install-qlt.outputs.qlt-home }}" qlt version From 9de877933fdb63ab9273a7368150dff44397fb76 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Fri, 9 Feb 2024 15:15:50 -0500 Subject: [PATCH 20/44] spacing --- .github/workflows/run-codeql-unit-tests-cpp.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-codeql-unit-tests-cpp.yml b/.github/workflows/run-codeql-unit-tests-cpp.yml index 77627a6..b197884 100644 --- a/.github/workflows/run-codeql-unit-tests-cpp.yml +++ b/.github/workflows/run-codeql-unit-tests-cpp.yml @@ -71,7 +71,8 @@ jobs: shell: bash run: | echo -e "Checking CodeQL Version:" - $QLT_CODEQL_PATH --version + $QLT_CODEQL_PATH --version + echo -e "Checking QLT Version:" echo "QLT Home: ${{ steps.install-qlt.outputs.qlt-home }}" qlt version From 7fe2b9a456bba57ea4f3daf8c205091b857042a0 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Fri, 9 Feb 2024 15:16:19 -0500 Subject: [PATCH 21/44] template fixes --- .../Test/Actions/run-unit-tests.liquid | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/CodeQLToolkit.Features/Templates/Test/Actions/run-unit-tests.liquid b/src/CodeQLToolkit.Features/Templates/Test/Actions/run-unit-tests.liquid index 321c760..08ceffe 100644 --- a/src/CodeQLToolkit.Features/Templates/Test/Actions/run-unit-tests.liquid +++ b/src/CodeQLToolkit.Features/Templates/Test/Actions/run-unit-tests.liquid @@ -78,19 +78,20 @@ jobs: {% raw %} - name: Install CodeQL id: install-codeql - uses: ./.github/actions/install-codeql - with: - codeql-cli-version: ${{ matrix.codeql_cli }} - codeql-stdlib-version: ${{ matrix.codeql_standard_library }} - add-to-path: true + shell: bash + run: | + echo "Installing CodeQL" + qlt codeql run install --base example/ + echo "-----------------------------" + echo "CodeQL Home: $QLT_CODEQL_HOME" + echo "CodeQL Binary: $QLT_CODEQL_PATH" - name: Verify Versions of Tooling shell: bash run: | - echo "CodeQL Home: ${{ steps.install-codeql.outputs.codeql-home }}" echo -e "Checking CodeQL Version:" - codeql --version - + $QLT_CODEQL_PATH --version + echo -e "Checking QLT Version:" echo "QLT Home: ${{ steps.install-qlt.outputs.qlt-home }}" qlt version From 2f5b24c264867f2a49a70c3f1c7ec0e8f5d317b9 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Fri, 9 Feb 2024 15:23:44 -0500 Subject: [PATCH 22/44] update query vailidation --- .../workflows/validate-codeql-queries-cpp.yml | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/.github/workflows/validate-codeql-queries-cpp.yml b/.github/workflows/validate-codeql-queries-cpp.yml index c981d03..24dbb99 100644 --- a/.github/workflows/validate-codeql-queries-cpp.yml +++ b/.github/workflows/validate-codeql-queries-cpp.yml @@ -58,24 +58,27 @@ jobs: - name: Install CodeQL - uses: ./.github/actions/install-codeql - with: - codeql-cli-version: ${{ matrix.codeql_cli }} - codeql-stdlib-version: ${{ matrix.codeql_standard_library }} - add-to-path: true + id: install-codeql + shell: bash + run: | + echo "Installing CodeQL" + qlt codeql run install --base example/ + echo "-----------------------------" + echo "CodeQL Home: $QLT_CODEQL_HOME" + echo "CodeQL Binary: $QLT_CODEQL_PATH" - name: Verify Versions of Tooling shell: bash run: | - echo "CodeQL Home: ${{ steps.install-codeql.outputs.codeql-home }}" echo -e "Checking CodeQL Version:" - codeql --version - + $QLT_CODEQL_PATH --version + echo -e "Checking QLT Version:" echo "QLT Home: ${{ steps.install-qlt.outputs.qlt-home }}" qlt version + - name: Install QL Packs shell: bash run: | From c3e4ed2d8ab8a09d6dc811b804b17621b8fa2235 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Fri, 9 Feb 2024 15:27:49 -0500 Subject: [PATCH 23/44] update template --- .../Actions/validate-query-metadata.liquid | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/CodeQLToolkit.Features/Templates/Validation/Actions/validate-query-metadata.liquid b/src/CodeQLToolkit.Features/Templates/Validation/Actions/validate-query-metadata.liquid index 7f02cda..234b5e4 100644 --- a/src/CodeQLToolkit.Features/Templates/Validation/Actions/validate-query-metadata.liquid +++ b/src/CodeQLToolkit.Features/Templates/Validation/Actions/validate-query-metadata.liquid @@ -77,19 +77,21 @@ jobs: {% endif %} {% raw %} - name: Install CodeQL - uses: ./.github/actions/install-codeql - with: - codeql-cli-version: ${{ matrix.codeql_cli }} - codeql-stdlib-version: ${{ matrix.codeql_standard_library }} - add-to-path: true + id: install-codeql + shell: bash + run: | + echo "Installing CodeQL" + qlt codeql run install --base example/ + echo "-----------------------------" + echo "CodeQL Home: $QLT_CODEQL_HOME" + echo "CodeQL Binary: $QLT_CODEQL_PATH" - name: Verify Versions of Tooling shell: bash run: | - echo "CodeQL Home: ${{ steps.install-codeql.outputs.codeql-home }}" echo -e "Checking CodeQL Version:" - codeql --version - + $QLT_CODEQL_PATH --version + echo -e "Checking QLT Version:" echo "QLT Home: ${{ steps.install-qlt.outputs.qlt-home }}" qlt version From df6913831ecafd401c77574f43c04521bda170ca Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Fri, 9 Feb 2024 15:33:32 -0500 Subject: [PATCH 24/44] fix path binary --- .../Commands/Targets/CheckQueriesCommandTarget.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/CodeQLToolkit.Features/Validation/Commands/Targets/CheckQueriesCommandTarget.cs b/src/CodeQLToolkit.Features/Validation/Commands/Targets/CheckQueriesCommandTarget.cs index cda273e..70eafd5 100644 --- a/src/CodeQLToolkit.Features/Validation/Commands/Targets/CheckQueriesCommandTarget.cs +++ b/src/CodeQLToolkit.Features/Validation/Commands/Targets/CheckQueriesCommandTarget.cs @@ -1,5 +1,6 @@ using CodeQLToolkit.Features.Query.Commands.Targets; using CodeQLToolkit.Features.Validation.Models; +using CodeQLToolkit.Shared.CodeQL; using Newtonsoft.Json; using System; using System.Collections.Generic; @@ -20,9 +21,12 @@ public override void Run() { Log.G().LogInformation($"Validating query metadata for {Language}..."); + var installation = CodeQLInstallation.LoadFromConfig(Base); + + using (Process process = new Process()) { - process.StartInfo.FileName = "codeql"; + process.StartInfo.FileName = installation.CodeQLToolBinary; process.StartInfo.UseShellExecute = false; process.StartInfo.WorkingDirectory = Base; process.StartInfo.RedirectStandardOutput = true; From 574cee0bef7a466b5e9c81071fb1c66f65df32e0 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Fri, 9 Feb 2024 15:38:15 -0500 Subject: [PATCH 25/44] don't target every branch --- .github/workflows/internal-validate-workflow-files.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/internal-validate-workflow-files.yml b/.github/workflows/internal-validate-workflow-files.yml index 4c08a59..8cfbfad 100644 --- a/.github/workflows/internal-validate-workflow-files.yml +++ b/.github/workflows/internal-validate-workflow-files.yml @@ -3,10 +3,10 @@ name: ⚙️ Validate Workflow Files on: push: branches: - - '**' + - 'main' pull_request: branches: - - '**' + - 'main' workflow_dispatch: jobs: From ee0d8b83740f1d17039597f8fef22261fef49498 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Fri, 9 Feb 2024 15:46:39 -0500 Subject: [PATCH 26/44] linux specific test case --- .../Utils/FileUtilsTest.cs | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/test/CodeQLToolkit.Shared.Tests/Utils/FileUtilsTest.cs b/test/CodeQLToolkit.Shared.Tests/Utils/FileUtilsTest.cs index 1ab01df..7646290 100644 --- a/test/CodeQLToolkit.Shared.Tests/Utils/FileUtilsTest.cs +++ b/test/CodeQLToolkit.Shared.Tests/Utils/FileUtilsTest.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; @@ -34,24 +35,50 @@ public void TestCreateTempDirectoryWithPath() [Test] public void TestSanitizeFilename() { - string[] paths = new string[]{ + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + string[] paths = new string[]{ "invalid:#!/\\/path", "codeql/cli-1.1.2" - }; + }; - string[] expected = new string[]{ + string[] expected = new string[]{ "invalid_#!___path", "codeql_cli-1.1.2" - }; + }; + + for (int i = 0; i < paths.Length; i++) + { + Console.WriteLine(i + "Actual: " + FileUtils.SanitizeFilename(paths[i])); + Console.WriteLine(i + "Expected: " + expected[i]); - for (int i= 0; i < paths.Length; i++) + Assert.IsTrue(FileUtils.SanitizeFilename(paths[i]) == expected[i]); + } + + } + else { - Console.WriteLine(i + "Actual: " + FileUtils.SanitizeFilename(paths[i])); - Console.WriteLine(i + "Expected: " + expected[i]); + string[] paths = new string[]{ + "invalid:#!/\\/path", + "codeql/cli-1.1.2" + }; + + string[] expected = new string[]{ + "invalid:#!_\\_path", + "codeql_cli-1.1.2" + }; - Assert.IsTrue(FileUtils.SanitizeFilename(paths[i]) == expected[i]); + for (int i = 0; i < paths.Length; i++) + { + Console.WriteLine(i + "Actual: " + FileUtils.SanitizeFilename(paths[i])); + Console.WriteLine(i + "Expected: " + expected[i]); + + Assert.IsTrue(FileUtils.SanitizeFilename(paths[i]) == expected[i]); + } } + } } } From 8d43622cfb189ff0af1eb3e32f546cfa1d7aad44 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Fri, 9 Feb 2024 15:57:23 -0500 Subject: [PATCH 27/44] spacing --- .github/workflows/validate-codeql-queries-cpp.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/validate-codeql-queries-cpp.yml b/.github/workflows/validate-codeql-queries-cpp.yml index 24dbb99..8c0a6b8 100644 --- a/.github/workflows/validate-codeql-queries-cpp.yml +++ b/.github/workflows/validate-codeql-queries-cpp.yml @@ -78,7 +78,6 @@ jobs: qlt version - - name: Install QL Packs shell: bash run: | From ff30e7c2a32e9ef9840f0fd8bb248bc556ba67e8 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Tue, 20 Feb 2024 16:30:03 -0500 Subject: [PATCH 28/44] bundle integration --- example/cpp/stuff/src/codeql-pack.lock.yml | 18 ++ example/cpp/stuff/src/qlpack.yml | 6 +- example/cpp/stuff/test/codeql-pack.lock.yml | 18 ++ example/cpp/stuff2/src/codeql-pack.lock.yml | 18 ++ example/cpp/stuff2/src/qlpack.yml | 6 +- example/cpp/stuff2/test/codeql-pack.lock.yml | 18 ++ example/qlt.conf.json | 13 +- .../CodeQL/Commands/Targets/InstallCommand.cs | 11 +- .../CodeQL/CodeQLInstallation.cs | 189 ++++++++++++++++-- src/CodeQLToolkit.Shared/Utils/QLTConfig.cs | 2 + src/CodeQLToolkit.Shared/Utils/ToolUtil.cs | 43 ++++ 11 files changed, 308 insertions(+), 34 deletions(-) create mode 100644 example/cpp/stuff/src/codeql-pack.lock.yml create mode 100644 example/cpp/stuff/test/codeql-pack.lock.yml create mode 100644 example/cpp/stuff2/src/codeql-pack.lock.yml create mode 100644 example/cpp/stuff2/test/codeql-pack.lock.yml create mode 100644 src/CodeQLToolkit.Shared/Utils/ToolUtil.cs diff --git a/example/cpp/stuff/src/codeql-pack.lock.yml b/example/cpp/stuff/src/codeql-pack.lock.yml new file mode 100644 index 0000000..4edf97c --- /dev/null +++ b/example/cpp/stuff/src/codeql-pack.lock.yml @@ -0,0 +1,18 @@ +--- +lockVersion: 1.0.0 +dependencies: + codeql/cpp-all: + version: 0.12.2 + codeql/dataflow: + version: 0.1.5 + codeql/rangeanalysis: + version: 0.0.4 + codeql/ssa: + version: 0.2.5 + codeql/tutorial: + version: 0.2.5 + codeql/typetracking: + version: 0.2.5 + codeql/util: + version: 0.2.5 +compiled: false diff --git a/example/cpp/stuff/src/qlpack.yml b/example/cpp/stuff/src/qlpack.yml index 5163077..51c46db 100644 --- a/example/cpp/stuff/src/qlpack.yml +++ b/example/cpp/stuff/src/qlpack.yml @@ -1,7 +1,9 @@ +--- +library: true name: qlt55/stuff -version: 0.0.0 +version: 0.0.1 description: Default description suites: license: dependencies: - codeql/cpp-all: 0.3.5 \ No newline at end of file + codeql/cpp-all: "0.12.2" \ No newline at end of file diff --git a/example/cpp/stuff/test/codeql-pack.lock.yml b/example/cpp/stuff/test/codeql-pack.lock.yml new file mode 100644 index 0000000..4edf97c --- /dev/null +++ b/example/cpp/stuff/test/codeql-pack.lock.yml @@ -0,0 +1,18 @@ +--- +lockVersion: 1.0.0 +dependencies: + codeql/cpp-all: + version: 0.12.2 + codeql/dataflow: + version: 0.1.5 + codeql/rangeanalysis: + version: 0.0.4 + codeql/ssa: + version: 0.2.5 + codeql/tutorial: + version: 0.2.5 + codeql/typetracking: + version: 0.2.5 + codeql/util: + version: 0.2.5 +compiled: false diff --git a/example/cpp/stuff2/src/codeql-pack.lock.yml b/example/cpp/stuff2/src/codeql-pack.lock.yml new file mode 100644 index 0000000..4edf97c --- /dev/null +++ b/example/cpp/stuff2/src/codeql-pack.lock.yml @@ -0,0 +1,18 @@ +--- +lockVersion: 1.0.0 +dependencies: + codeql/cpp-all: + version: 0.12.2 + codeql/dataflow: + version: 0.1.5 + codeql/rangeanalysis: + version: 0.0.4 + codeql/ssa: + version: 0.2.5 + codeql/tutorial: + version: 0.2.5 + codeql/typetracking: + version: 0.2.5 + codeql/util: + version: 0.2.5 +compiled: false diff --git a/example/cpp/stuff2/src/qlpack.yml b/example/cpp/stuff2/src/qlpack.yml index e8bfc32..2dad82b 100644 --- a/example/cpp/stuff2/src/qlpack.yml +++ b/example/cpp/stuff2/src/qlpack.yml @@ -1,7 +1,9 @@ +--- +library: true name: qlt2/stuff2 -version: 0.0.0 +version: 0.0.1 description: Default description suites: license: dependencies: - codeql/cpp-all: 0.3.5 \ No newline at end of file + codeql/cpp-all: "0.12.2" \ No newline at end of file diff --git a/example/cpp/stuff2/test/codeql-pack.lock.yml b/example/cpp/stuff2/test/codeql-pack.lock.yml new file mode 100644 index 0000000..4edf97c --- /dev/null +++ b/example/cpp/stuff2/test/codeql-pack.lock.yml @@ -0,0 +1,18 @@ +--- +lockVersion: 1.0.0 +dependencies: + codeql/cpp-all: + version: 0.12.2 + codeql/dataflow: + version: 0.1.5 + codeql/rangeanalysis: + version: 0.0.4 + codeql/ssa: + version: 0.2.5 + codeql/tutorial: + version: 0.2.5 + codeql/typetracking: + version: 0.2.5 + codeql/util: + version: 0.2.5 +compiled: false diff --git a/example/qlt.conf.json b/example/qlt.conf.json index eb74899..2379f33 100644 --- a/example/qlt.conf.json +++ b/example/qlt.conf.json @@ -1,7 +1,10 @@ { - "CodeQLCLI": "2.11.6", - "CodeQLStandardLibrary": "codeql-cli/v2.11.6", - "CodeQLCLIBundle": "codeql-bundle-20221211", - "EnableCustomCodeQLBundles": false, - "CodeQLStandardLibraryIdent": "codeql-cli_v2.11.6" + "CodeQLCLI": "2.15.5", + "CodeQLStandardLibrary": "codeql-cli/v2.15.5", + "CodeQLCLIBundle": "codeql-bundle-v2.15.5", + "EnableCustomCodeQLBundles": true, + "CodeQLStandardLibraryIdent": "codeql-cli_v2.15.5", + "ExportedCustomizationPacks" : [ + "qlt2/stuff2" + ] } \ No newline at end of file diff --git a/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs b/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs index e2e034c..0243bca 100644 --- a/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs +++ b/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs @@ -21,7 +21,9 @@ public override void Run() Log.G().LogInformation($"Checking for installation..."); - if (installation.IsInstalled()) + // if it is the case that it is installed but we are in custom bundle mode we RE install it. + + if (installation.IsInstalled() && !installation.EnableCustomCodeQLBundles) { Log.G().LogInformation($"CodeQL is already installed at that version. Please delete the installation directory to reinstall."); } @@ -40,8 +42,11 @@ public override void Run() if (AutomationTypeHelper.AutomationTypeFromString(AutomationTarget) == AutomationType.ACTIONS) { - File.AppendAllText(Environment.GetEnvironmentVariable("GITHUB_ENV"), $"QLT_CODEQL_HOME={installation.CodeQLHome}" + "\n"); - File.AppendAllText(Environment.GetEnvironmentVariable("GITHUB_ENV"), $"QLT_CODEQL_PATH={installation.CodeQLToolBinary}" + "\n"); + if (Environment.GetEnvironmentVariable("GITHUB_ENV") != null && File.Exists(Environment.GetEnvironmentVariable("GITHUB_ENV"))) + { + File.AppendAllText(Environment.GetEnvironmentVariable("GITHUB_ENV"), $"QLT_CODEQL_HOME={installation.CodeQLHome}" + "\n"); + File.AppendAllText(Environment.GetEnvironmentVariable("GITHUB_ENV"), $"QLT_CODEQL_PATH={installation.CodeQLToolBinary}" + "\n"); + } } } diff --git a/src/CodeQLToolkit.Shared/CodeQL/CodeQLInstallation.cs b/src/CodeQLToolkit.Shared/CodeQL/CodeQLInstallation.cs index 0260e68..86359d5 100644 --- a/src/CodeQLToolkit.Shared/CodeQL/CodeQLInstallation.cs +++ b/src/CodeQLToolkit.Shared/CodeQL/CodeQLInstallation.cs @@ -5,7 +5,8 @@ using CodeQLToolkit.Shared.Logging; using LibGit2Sharp; using System.IO.Compression; -using System.Runtime.CompilerServices; +using System.Diagnostics; + namespace CodeQLToolkit.Shared.CodeQL @@ -17,6 +18,7 @@ public class CodeQLInstallation public string CLIBundle { get; set; } public string StandardLibraryIdent { get; set; } public bool EnableCustomCodeQLBundles { get; set; } + public string[] ExportedCustomizationPacks { get; set; } public string Base { get; set; } public static CodeQLInstallation LoadFromConfig(string Base) @@ -26,7 +28,7 @@ public static CodeQLInstallation LoadFromConfig(string Base) Base = Base }; - return LoadFromConfig(c.FromFile()); + return LoadFromConfig(c.FromFile()); } public static CodeQLInstallation LoadFromConfig(QLTConfig c) @@ -41,6 +43,7 @@ public static CodeQLInstallation LoadFromConfig(QLTConfig c) CLIBundle = config.CodeQLCLIBundle, StandardLibraryIdent = config.CodeQLStandardLibraryIdent, StandardLibraryVersion = config.CodeQLStandardLibrary, + ExportedCustomizationPacks = config.ExportedCustomizationPacks, Base = config.Base }; @@ -126,14 +129,19 @@ public void Install() } - private void CustomBundleInstall() + public string CustomBundleOutputBundle { - throw new NotImplementedException(); + get + { + return Path.Combine(CustomBundleOutputDirectory, "codeql-bundle.tar.gz"); + } } - - private void BundleInstall() + public string CustomBundleOutputDirectory { - throw new NotImplementedException(); + get + { + return Path.Combine(InstallationDirectory, "out"); + } } public string StdLibDirectory @@ -204,7 +212,132 @@ private void PackageInstall() } } - + private void CustomBundleInstall() + { + Log.G().LogInformation($"Begin Installation "); + Log.G().LogInformation($"Requested Base Bundle Version {CLIBundle}"); + Log.G().LogInformation($"Create installation directory {InstallationDirectory}"); + + if (!Directory.Exists(InstallationDirectory)) + { + Directory.CreateDirectory(InstallationDirectory); + } + + Log.G().LogInformation($"Download CodeQL Bundle Base..."); + + var downloadFile = $"codeql-bundle-{PlatformID}.tar.gz"; + var customBundleSource = Path.Combine(InstallationDirectory, downloadFile); + + Log.G().LogInformation($"Checking if a existing source bundle is present..."); + + if (File.Exists(customBundleSource)) + { + Log.G().LogInformation($"Bundle exists, will skip download"); + } + else + { + using (var client = new WebClient()) + { + string uri = $"https://github.com/github/codeql-action/releases/download/{CLIBundle}/{downloadFile}"; + Log.G().LogInformation($"Remote URL: {uri}..."); + + client.DownloadFile(uri, customBundleSource); + } + } + + // next, we create the output directory that will contain the bundle. + // if it exists, we remove it + Log.G().LogInformation($"Checking for custom bundle output directory..."); + + if (Directory.Exists(CustomBundleOutputDirectory)) + { + Log.G().LogInformation($"Exists. Will remove."); + Directory.Delete(CustomBundleOutputDirectory, true); + Directory.CreateDirectory(CustomBundleOutputDirectory); + } + else + { + Directory.CreateDirectory(CustomBundleOutputDirectory); + } + + var workingDirectory = Path.GetFullPath(Base); + + if(ExportedCustomizationPacks == null || ExportedCustomizationPacks.Length == 0) + { + throw new Exception("No packs are set to be exported. Please add at least one pack to export in your `qlt.conf.json` file under the property `ExportedCustomizationPacks`."); + } + + Log.G().LogInformation($"Building custom bundle. This may take a while..."); + + var packs = string.Join(" ", ExportedCustomizationPacks); + // next, we run the bundling tool. + // typical command line: + // codeql_bundle -b .\scratch\codeql-bundle-win64.tar.gz -o scratch\out -w .\tests\workspace\ --help + using (Process process = new Process()) + { + process.StartInfo.FileName = ToolUtil.GetTool("codeql_bundle"); + process.StartInfo.WorkingDirectory = workingDirectory; + process.StartInfo.UseShellExecute = false; + process.StartInfo.RedirectStandardOutput = false; + process.StartInfo.RedirectStandardError = false; + process.StartInfo.Arguments = $"-b {customBundleSource} -o {CustomBundleOutputDirectory} -w {workingDirectory} {packs}"; + + process.Start(); + + process.WaitForExit(); + + if (process.ExitCode != 0) + { + Log.G().LogInformation($"Failure running bundle command."); + throw new Exception("Failure running bundle command."); + } + } + + // once that is complete we expand the archive that is generated. + // it will be `codeql-bundle.tar.gz`. + // + // we will extract it to `InstallationDirectory` since `codeql` will be created by the extraction. + + Log.G().LogInformation($"Done. Checking for existance of {CodeQLDirectory}"); + + if (Directory.Exists(CodeQLDirectory)) + { + Log.G().LogInformation($"Exists. Will delete {CodeQLDirectory}"); + Directory.Delete(CodeQLDirectory, true); + } + + // Using SharpCompress, extract the tar.gz bundle to the `InstallationDirectory`. + Log.G().LogInformation($"Extracting bundle to {InstallationDirectory}..."); + + + using (Process process = new Process()) + { + process.StartInfo.FileName = ToolUtil.GetCommand("tar"); + process.StartInfo.WorkingDirectory = InstallationDirectory; + process.StartInfo.UseShellExecute = false; + process.StartInfo.RedirectStandardOutput = false; + process.StartInfo.RedirectStandardError = false; + process.StartInfo.Arguments = $"-zxf {CustomBundleOutputBundle} "; + + process.Start(); + + process.WaitForExit(); + + if (process.ExitCode != 0) + { + Log.G().LogInformation($"Failure running extract bundle command."); + throw new Exception("Failure running extract bundle command."); + } + } + + Log.G().LogInformation($"Done."); + } + + private void BundleInstall() + { + throw new NotImplementedException(); + } + private string GetIdentForPackage(ArtifactKind k) { @@ -214,34 +347,46 @@ private string GetIdentForPackage(ArtifactKind k) return StringUtils.CreateMD5(FileUtils.SanitizeFilename(ident)).ToLower(); } + if( k == ArtifactKind.CUSTOM_BUNDLE) + { + var ident = String.Join("", "codeql-bundle-" + CLIBundle); + return StringUtils.CreateMD5(FileUtils.SanitizeFilename(ident)).ToLower(); + } + throw new NotImplementedException(); } - + // TODO -- need to check environment variables to see if custom bundle is "installed" public bool IsInstalled() { Log.G().LogInformation($"Checking if codeql is installed..."); - Log.G().LogInformation($"Requested CLI Version {CLIVersion}, Standard Library Ident: {StandardLibraryIdent}"); + Log.G().LogInformation($"Requested CLI Version {CLIVersion}, Standard Library Ident: {StandardLibraryIdent}, CLIBundle: {CLIBundle}, Using Custom Bundles: {EnableCustomCodeQLBundles}"); - // if custom bundles are enabled - // they are doing local development or running - // unit tests. In either case, we want to make sure - // to reassemble the bundle. - if (EnableCustomCodeQLBundles) + Log.G().LogInformation($"Checking for existance of required directories..."); + + if (!Directory.Exists(InstallationDirectory)) { - Log.G().LogInformation($"Custom bundle mode."); + Log.G().LogInformation($"Installation directory {InstallationDirectory} is missing."); return false; } - Log.G().LogInformation($"CodeQL Package Mode"); - Log.G().LogInformation($"Checking for existance of directory {InstallationDirectory}"); + if (!Directory.Exists(CodeQLDirectory)) + { + Log.G().LogInformation($"CodeQL Directory Missing: {CodeQLDirectory}"); + return false; + } - if (Directory.Exists(InstallationDirectory) && Directory.Exists(CodeQLDirectory) && Directory.Exists(StdLibDirectory)) + // custom bundles don't have a standard library directory. + if (!EnableCustomCodeQLBundles) { - Log.G().LogInformation($"CodeQL Installation exists. CLI Version {CLIVersion}, Standard Library Ident: {StandardLibraryIdent}"); - return true; + if (!Directory.Exists(StdLibDirectory)) + { + Log.G().LogInformation($"Standard Library Directory Missing: {StdLibDirectory}"); + return false; + } + } - return false; + return true; } public string InstallationDirectory diff --git a/src/CodeQLToolkit.Shared/Utils/QLTConfig.cs b/src/CodeQLToolkit.Shared/Utils/QLTConfig.cs index 7c8a1a9..708a329 100644 --- a/src/CodeQLToolkit.Shared/Utils/QLTConfig.cs +++ b/src/CodeQLToolkit.Shared/Utils/QLTConfig.cs @@ -13,6 +13,8 @@ public class QLTConfig public string CodeQLStandardLibrary { get; set; } public string CodeQLCLIBundle { get; set; } + public string[] ExportedCustomizationPacks { get; set; } + public bool EnableCustomCodeQLBundles { get; set; } public string CodeQLStandardLibraryIdent { diff --git a/src/CodeQLToolkit.Shared/Utils/ToolUtil.cs b/src/CodeQLToolkit.Shared/Utils/ToolUtil.cs new file mode 100644 index 0000000..28fd017 --- /dev/null +++ b/src/CodeQLToolkit.Shared/Utils/ToolUtil.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace CodeQLToolkit.Shared.Utils +{ + public class ToolUtil + { + public static string ToolRoot + { + get + { + return Path.Combine(Utils.FileUtils.GetExecutingDirectory().FullName, "tools"); + } + } + + public static string ExecutableExtensionForPlatform + { + get + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return ".exe"; + } + + return ""; + } + } + + public static string GetTool(string toolName) + { + return Path.Combine(ToolRoot, toolName + ExecutableExtensionForPlatform); + } + + public static string GetCommand(string toolName) + { + return Path.Combine(toolName + ExecutableExtensionForPlatform); + } + } +} From ed856968bb9bb07be3eccf9025f2de4e68e1de21 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Wed, 21 Feb 2024 12:28:48 -0500 Subject: [PATCH 29/44] bundle integration; quick bundles as well as correct processing from configuration file. --- src/CodeQLToolkit.Core/Main.cs | 1 + .../Lifecycle/BundleLifecycleFeature.cs | 12 +-- ...abledCustomCodeQLBundlesLifecycleTarget.cs | 2 +- ...sableCustomCodeQLBundlesLifecycleTarget.cs | 2 +- ...nableCustomCodeQLBundlesLifecycleTarget.cs | 2 +- .../CodeQL/Commands/CodeQLCommandFeature.cs | 21 +++-- .../CodeQL/Commands/Targets/InstallCommand.cs | 23 +++++- .../Query/Commands/QueryCommandFeature.cs | 3 +- .../Targets/InstallQueryPacksCommandTarget.cs | 5 ++ .../Actions/ExecuteUnitTestsCommandTarget.cs | 6 +- .../Test/Commands/TestCommandFeature.cs | 6 +- .../Targets/CheckQueriesCommandTarget.cs | 3 + .../Commands/ValidationCommandFeature.cs | 7 +- .../CodeQL/CodeQLInstallation.cs | 47 +++++++++++- src/CodeQLToolkit.Shared/CodeQL/RESOLUTION.md | 76 ++++++++++++++----- src/CodeQLToolkit.Shared/Options/Globals.cs | 7 ++ src/CodeQLToolkit.Shared/Target/ITarget.cs | 2 + src/CodeQLToolkit.Shared/Utils/QLTConfig.cs | 2 - 18 files changed, 175 insertions(+), 52 deletions(-) diff --git a/src/CodeQLToolkit.Core/Main.cs b/src/CodeQLToolkit.Core/Main.cs index fda2c6a..b1e6442 100644 --- a/src/CodeQLToolkit.Core/Main.cs +++ b/src/CodeQLToolkit.Core/Main.cs @@ -29,6 +29,7 @@ public static async Task Main(string[] args) rootCommand.AddGlobalOption(Globals.BasePathOption); rootCommand.AddGlobalOption(Globals.AutomationTypeOption); rootCommand.AddGlobalOption(Globals.Development); + rootCommand.AddGlobalOption(Globals.UseBundle); var versionCommand = new Command("version", "Get the current tool version."); rootCommand.Add(versionCommand); diff --git a/src/CodeQLToolkit.Features/Bundle/Lifecycle/BundleLifecycleFeature.cs b/src/CodeQLToolkit.Features/Bundle/Lifecycle/BundleLifecycleFeature.cs index 27611d2..9e12242 100644 --- a/src/CodeQLToolkit.Features/Bundle/Lifecycle/BundleLifecycleFeature.cs +++ b/src/CodeQLToolkit.Features/Bundle/Lifecycle/BundleLifecycleFeature.cs @@ -32,22 +32,22 @@ public override LanguageType[] SupportedLangauges public void Register(Command parentCommand) { - Log.G().LogInformation("Registering lifecycle submodule."); + //Log.G().LogInformation("Registering lifecycle submodule."); var setCommand = new Command("set", "Functions pertaining to setting variables related to custom CodeQL bundles."); - parentCommand.Add(setCommand); + //parentCommand.Add(setCommand); var enableCommand = new Command("enable-custom-bundles", "Enables custom CodeQL Bundles."); - setCommand.Add(enableCommand); + //setCommand.Add(enableCommand); var disableCommand = new Command("disable-custom-bundles", "Disables custom CodeQL Bundles."); - setCommand.Add(disableCommand); + //setCommand.Add(disableCommand); var getCommand = new Command("get", "Functions pertaining to getting variables related to CodeQL Bundles."); - parentCommand.Add(getCommand); + //parentCommand.Add(getCommand); var getEnabledCommand = new Command("enabled", "Determines if custom CodeQL Bundles are enabled."); - getCommand.Add(getEnabledCommand); + //getCommand.Add(getEnabledCommand); { enableCommand.SetHandler((basePath) => diff --git a/src/CodeQLToolkit.Features/Bundle/Lifecycle/Targets/GetEnabledCustomCodeQLBundlesLifecycleTarget.cs b/src/CodeQLToolkit.Features/Bundle/Lifecycle/Targets/GetEnabledCustomCodeQLBundlesLifecycleTarget.cs index 9e2e4d6..31f20dc 100644 --- a/src/CodeQLToolkit.Features/Bundle/Lifecycle/Targets/GetEnabledCustomCodeQLBundlesLifecycleTarget.cs +++ b/src/CodeQLToolkit.Features/Bundle/Lifecycle/Targets/GetEnabledCustomCodeQLBundlesLifecycleTarget.cs @@ -22,7 +22,7 @@ override public void Run() var config = c.FromFile(); Console.WriteLine($"---------current settings---------"); - Console.WriteLine($"CodeQL Custom Bundles Enabled: {config.EnableCustomCodeQLBundles}"); + //Console.WriteLine($"CodeQL Custom Bundles Enabled: {config.EnableCustomCodeQLBundles}"); Console.WriteLine($"----------------------------------"); Console.WriteLine("(hint: use `qlt bundle set` to modify these values.)"); diff --git a/src/CodeQLToolkit.Features/Bundle/Lifecycle/Targets/SetDisableCustomCodeQLBundlesLifecycleTarget.cs b/src/CodeQLToolkit.Features/Bundle/Lifecycle/Targets/SetDisableCustomCodeQLBundlesLifecycleTarget.cs index dee5072..2a5162e 100644 --- a/src/CodeQLToolkit.Features/Bundle/Lifecycle/Targets/SetDisableCustomCodeQLBundlesLifecycleTarget.cs +++ b/src/CodeQLToolkit.Features/Bundle/Lifecycle/Targets/SetDisableCustomCodeQLBundlesLifecycleTarget.cs @@ -21,7 +21,7 @@ override public void Run() var config = c.FromFile(); - config.EnableCustomCodeQLBundles = false; + //config.EnableCustomCodeQLBundles = false; config.ToFile(); diff --git a/src/CodeQLToolkit.Features/Bundle/Lifecycle/Targets/SetEnableCustomCodeQLBundlesLifecycleTarget.cs b/src/CodeQLToolkit.Features/Bundle/Lifecycle/Targets/SetEnableCustomCodeQLBundlesLifecycleTarget.cs index a59a1c7..09802da 100644 --- a/src/CodeQLToolkit.Features/Bundle/Lifecycle/Targets/SetEnableCustomCodeQLBundlesLifecycleTarget.cs +++ b/src/CodeQLToolkit.Features/Bundle/Lifecycle/Targets/SetEnableCustomCodeQLBundlesLifecycleTarget.cs @@ -21,7 +21,7 @@ override public void Run() var config = c.FromFile(); - config.EnableCustomCodeQLBundles = true; + //config.EnableCustomCodeQLBundles = true; config.ToFile(); diff --git a/src/CodeQLToolkit.Features/CodeQL/Commands/CodeQLCommandFeature.cs b/src/CodeQLToolkit.Features/CodeQL/Commands/CodeQLCommandFeature.cs index acbf71f..1138781 100644 --- a/src/CodeQLToolkit.Features/CodeQL/Commands/CodeQLCommandFeature.cs +++ b/src/CodeQLToolkit.Features/CodeQL/Commands/CodeQLCommandFeature.cs @@ -39,15 +39,17 @@ public void Register(Command parentCommand) parentCommand.Add(runCommand); var installCommand = new Command("install", "Installs CodeQL (bundle or release distribution) locally."); - //var useCommand = new Command("use", "Switches tooling to use a different CodeQL version and updates the paths accordingly."); - //var listCommand = new Command("list", "Lists versions of CodeQL available locally."); + var customBundleOption = new Option("--custom-bundle", () => false, "Build a custom bundle and compile the bundle.") { IsRequired = true}; + var quickBundleOption = new Option("--quick-bundle", () => false, "Build a custom bundle and DO NOT compile the bundle.") { IsRequired = true}; + var packsOption = new Option("--packs", "When creating bundles, this specifies the packs to include, Example `pack1 pack2 pack3`. You may specify also as `--pack pack1 --pack2 --pack3`") { IsRequired = false, AllowMultipleArgumentsPerToken = true }; - runCommand.Add(installCommand); - //runCommand.Add(useCommand); - //runCommand.Add(listCommand); + installCommand.Add(customBundleOption); + installCommand.Add(quickBundleOption); + installCommand.Add(packsOption); - - installCommand.SetHandler((basePath, automationType) => + runCommand.Add(installCommand); + + installCommand.SetHandler((basePath, automationType, customBundleOption, quickBundleOption, packs) => { Log.G().LogInformation("Executing install command..."); @@ -55,10 +57,13 @@ public void Register(Command parentCommand) { Base = basePath, AutomationTarget = automationType, + CustomBundles = customBundleOption, + QuickBundles = quickBundleOption, + Packs = packs }.Run(); - }, Globals.BasePathOption, Globals.AutomationTypeOption); + }, Globals.BasePathOption, Globals.AutomationTypeOption, customBundleOption, quickBundleOption, packsOption); } public int Run() diff --git a/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs b/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs index 0243bca..1c1abb0 100644 --- a/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs +++ b/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs @@ -11,13 +11,33 @@ namespace CodeQLToolkit.Features.CodeQL.Commands.Targets { public class InstallCommand : CommandTarget { - + public bool CustomBundles { get; set; } + public bool QuickBundles { get; set; } + public string[] Packs { get; set; } public override void Run() { Log.G().LogInformation($"Running Install command"); // First, check if CodeQL is installed. var installation = CodeQLInstallation.LoadFromConfig(Base); + if(CustomBundles || QuickBundles) + { + installation.EnableCustomCodeQLBundles = true; + if (Packs!=null && Packs.Length > 0) + { + Log.G().LogInformation($"Overriding Packs on the command line. The following Packs will be packaged:"); + installation.ExportedCustomizationPacks = Packs; + } + else + { + Log.G().LogInformation($"Using `qlt.conf.json` to build list of packs to bundle. The following Packs will be packaged:"); + } + + installation.LogPacksToBeBuilt(); + + + installation.QuickBundle = QuickBundles; + } Log.G().LogInformation($"Checking for installation..."); @@ -32,7 +52,6 @@ public override void Run() Log.G().LogInformation($"Installing CodeQL..."); installation.Install(); - // set the environment variable Log.G().LogInformation($"Setting QLT_CODEQL_HOME to {installation.CodeQLHome}..."); Log.G().LogInformation($"Setting QLT_CODEQL_PATH to {installation.CodeQLToolBinary}..."); diff --git a/src/CodeQLToolkit.Features/Query/Commands/QueryCommandFeature.cs b/src/CodeQLToolkit.Features/Query/Commands/QueryCommandFeature.cs index d5bb08c..ff44aa2 100644 --- a/src/CodeQLToolkit.Features/Query/Commands/QueryCommandFeature.cs +++ b/src/CodeQLToolkit.Features/Query/Commands/QueryCommandFeature.cs @@ -30,7 +30,8 @@ public void Register(Command parentCommand) runCommand.Add(installPacksQueryCommand); - installPacksQueryCommand.SetHandler((basePath) => new InstallQueryPacksCommandTarget() { Base = basePath }.Run(), Globals.BasePathOption); + installPacksQueryCommand.SetHandler( + (basePath, useBundle) => new InstallQueryPacksCommandTarget() { Base = basePath, UseBundle = useBundle}.Run(), Globals.BasePathOption, Globals.UseBundle); } diff --git a/src/CodeQLToolkit.Features/Query/Commands/Targets/InstallQueryPacksCommandTarget.cs b/src/CodeQLToolkit.Features/Query/Commands/Targets/InstallQueryPacksCommandTarget.cs index a7bfcd0..4752c13 100644 --- a/src/CodeQLToolkit.Features/Query/Commands/Targets/InstallQueryPacksCommandTarget.cs +++ b/src/CodeQLToolkit.Features/Query/Commands/Targets/InstallQueryPacksCommandTarget.cs @@ -1,4 +1,5 @@ using CodeQLToolkit.Shared.CodeQL; +using Microsoft.VisualBasic; using System; using System.Collections.Generic; using System.Diagnostics; @@ -23,6 +24,10 @@ public override void Run() var installation = CodeQLInstallation.LoadFromConfig(Base); + installation.EnableCustomCodeQLBundles = UseBundle; + + installation.IsInstalledOrDie(); + foreach ( string file in files ) { Log.G().LogInformation($"Installing qlpack {file}..."); diff --git a/src/CodeQLToolkit.Features/Test/Commands/Targets/Actions/ExecuteUnitTestsCommandTarget.cs b/src/CodeQLToolkit.Features/Test/Commands/Targets/Actions/ExecuteUnitTestsCommandTarget.cs index 282d396..a8e4663 100644 --- a/src/CodeQLToolkit.Features/Test/Commands/Targets/Actions/ExecuteUnitTestsCommandTarget.cs +++ b/src/CodeQLToolkit.Features/Test/Commands/Targets/Actions/ExecuteUnitTestsCommandTarget.cs @@ -75,11 +75,9 @@ public override void Run() // Get A Copy of the installation var installation = CodeQLInstallation.LoadFromConfig(Base); - if(!installation.IsInstalled()) - { - DieWithError($"Requested CodeQL Version ({CLIVersion}) Not Installed. Run `qlt codeql run install` before running this step."); - } + installation.EnableCustomCodeQLBundles = UseBundle; + installation.IsInstalledOrDie(); using (Process process = new Process()) { diff --git a/src/CodeQLToolkit.Features/Test/Commands/TestCommandFeature.cs b/src/CodeQLToolkit.Features/Test/Commands/TestCommandFeature.cs index f9866ee..c3b661a 100644 --- a/src/CodeQLToolkit.Features/Test/Commands/TestCommandFeature.cs +++ b/src/CodeQLToolkit.Features/Test/Commands/TestCommandFeature.cs @@ -94,7 +94,7 @@ public void Register(Command parentCommand) }, Globals.BasePathOption, Globals.AutomationTypeOption, matrixOSVersion); //stdLibIdent - unitTestsCommand.SetHandler((basePath, automationType, numThreads, workDirectory, language, runnerOS, extraArgs) => { + unitTestsCommand.SetHandler((basePath, automationType, numThreads, workDirectory, language, runnerOS, extraArgs, useBundle) => { Log.G().LogInformation("Executing execute-unit-tests command..."); @@ -125,6 +125,7 @@ public void Register(Command parentCommand) featureTarget.CLIVersion = config.CodeQLCLI; featureTarget.STDLibIdent = config.CodeQLStandardLibraryIdent; featureTarget.ExtraCodeQLArgs = extraArgs; + featureTarget.UseBundle = useBundle; featureTarget.Run(); @@ -134,7 +135,8 @@ public void Register(Command parentCommand) workDirectoryOption, languageOption, runnerOSOption, - extraCodeQLOptions + extraCodeQLOptions, + Globals.UseBundle ); diff --git a/src/CodeQLToolkit.Features/Validation/Commands/Targets/CheckQueriesCommandTarget.cs b/src/CodeQLToolkit.Features/Validation/Commands/Targets/CheckQueriesCommandTarget.cs index 70eafd5..0648339 100644 --- a/src/CodeQLToolkit.Features/Validation/Commands/Targets/CheckQueriesCommandTarget.cs +++ b/src/CodeQLToolkit.Features/Validation/Commands/Targets/CheckQueriesCommandTarget.cs @@ -23,6 +23,9 @@ public override void Run() var installation = CodeQLInstallation.LoadFromConfig(Base); + installation.EnableCustomCodeQLBundles = UseBundle; + + installation.IsInstalledOrDie(); using (Process process = new Process()) { diff --git a/src/CodeQLToolkit.Features/Validation/Commands/ValidationCommandFeature.cs b/src/CodeQLToolkit.Features/Validation/Commands/ValidationCommandFeature.cs index b5d8450..31eae45 100644 --- a/src/CodeQLToolkit.Features/Validation/Commands/ValidationCommandFeature.cs +++ b/src/CodeQLToolkit.Features/Validation/Commands/ValidationCommandFeature.cs @@ -41,7 +41,7 @@ public void Register(Command parentCommand) runCommand.Add(checkQueryQueriesCommand); - checkQueryQueriesCommand.SetHandler((language, basePath, prettyPrint) => + checkQueryQueriesCommand.SetHandler((language, basePath, prettyPrint, useBundle) => { Log.G().LogInformation("Executing check-query-metadata command..."); @@ -49,10 +49,11 @@ public void Register(Command parentCommand) { Base = basePath, Language = language, - PrettyPrint = prettyPrint, + PrettyPrint = prettyPrint, + UseBundle = useBundle }.Run(); - }, languageOption, Globals.BasePathOption, prettyPrintOption); + }, languageOption, Globals.BasePathOption, prettyPrintOption, Globals.UseBundle); } public int Run() diff --git a/src/CodeQLToolkit.Shared/CodeQL/CodeQLInstallation.cs b/src/CodeQLToolkit.Shared/CodeQL/CodeQLInstallation.cs index 86359d5..f9cf8d1 100644 --- a/src/CodeQLToolkit.Shared/CodeQL/CodeQLInstallation.cs +++ b/src/CodeQLToolkit.Shared/CodeQL/CodeQLInstallation.cs @@ -19,6 +19,7 @@ public class CodeQLInstallation public string StandardLibraryIdent { get; set; } public bool EnableCustomCodeQLBundles { get; set; } public string[] ExportedCustomizationPacks { get; set; } + public bool QuickBundle { get; set; } public string Base { get; set; } public static CodeQLInstallation LoadFromConfig(string Base) @@ -38,7 +39,6 @@ public static CodeQLInstallation LoadFromConfig(QLTConfig c) return new CodeQLInstallation { - EnableCustomCodeQLBundles = config.EnableCustomCodeQLBundles, CLIVersion = config.CodeQLCLI, CLIBundle = config.CodeQLCLIBundle, StandardLibraryIdent = config.CodeQLStandardLibraryIdent, @@ -50,6 +50,17 @@ public static CodeQLInstallation LoadFromConfig(QLTConfig c) } + public void LogPacksToBeBuilt() + { + if(ExportedCustomizationPacks != null) + { + foreach(var p in ExportedCustomizationPacks) + { + Log.G().LogInformation($"Pack: {p}"); + } + } + } + public ArtifactKind Kind { get @@ -280,7 +291,15 @@ private void CustomBundleInstall() process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardOutput = false; process.StartInfo.RedirectStandardError = false; - process.StartInfo.Arguments = $"-b {customBundleSource} -o {CustomBundleOutputDirectory} -w {workingDirectory} {packs}"; + if (QuickBundle) + { + Log.G().LogInformation($"Note: Quick Bundles enabled and pre-compilation will be disabled..."); + process.StartInfo.Arguments = $"-nc -b {customBundleSource} -o {CustomBundleOutputDirectory} -w {workingDirectory} {packs}"; + } + else + { + process.StartInfo.Arguments = $"-b {customBundleSource} -o {CustomBundleOutputDirectory} -w {workingDirectory} {packs}"; + } process.Start(); @@ -355,12 +374,32 @@ private string GetIdentForPackage(ArtifactKind k) throw new NotImplementedException(); } - // TODO -- need to check environment variables to see if custom bundle is "installed" + + public bool IsInstalledOrDie() + { + var installed = IsInstalled(); + + if (!installed) + { + ProcessUtils.DieWithError("CodeQL not installed. Please run `qlt codeql run install --help` for more info."); + } + + return true; + } + public bool IsInstalled() { Log.G().LogInformation($"Checking if codeql is installed..."); - Log.G().LogInformation($"Requested CLI Version {CLIVersion}, Standard Library Ident: {StandardLibraryIdent}, CLIBundle: {CLIBundle}, Using Custom Bundles: {EnableCustomCodeQLBundles}"); + if (EnableCustomCodeQLBundles) + { + Log.G().LogInformation($"Requested Custom Bundle Based on Bundle {CLIBundle}"); + } + else + { + Log.G().LogInformation($"Requested CLI Version {CLIVersion} with Standard Library Ident: {StandardLibraryIdent}"); + } + Log.G().LogInformation($"Checking for existance of required directories..."); if (!Directory.Exists(InstallationDirectory)) diff --git a/src/CodeQLToolkit.Shared/CodeQL/RESOLUTION.md b/src/CodeQLToolkit.Shared/CodeQL/RESOLUTION.md index 3878f3d..36bd990 100644 --- a/src/CodeQLToolkit.Shared/CodeQL/RESOLUTION.md +++ b/src/CodeQLToolkit.Shared/CodeQL/RESOLUTION.md @@ -1,11 +1,12 @@ -# How QLT Resolves and Uses Standard CodeQL installations and Custom bundles. +# How Resolution Works + +## How QLT Resolves and Uses Standard CodeQL installations and Custom bundles. The layout of the installation repository is as follows: ``` -.qlt/repo/packages/ident <-- normal CodeQL installs -.qlt/repo/custom-bundle/ident <-- custom bundles -.qlt/repo/bundle/ident <-- precompiled CodeQL bundles which include the cli and library. +.qlt/repo/packages/ident/codeql <-- normal CodeQL installs +.qlt/repo/custom-bundle/ident/codeql <-- custom bundles ``` The general intention is that the tooling is used as follows: @@ -14,20 +15,61 @@ The general intention is that the tooling is used as follows: qlt codeql install ``` -If the `--use-quick-bundles` flag is specified, QLT will construct a custom bundle in the repository and then set the `QLT_CODEQL_PATH` -variable. This enables one to use this both in automation AND within -a local developer environment. +Will install the distribution version of CodeQL and: + +``` +qlt codeql install --custom-bundle [--packs pack1 pack2 pack3] +``` + +This will build and install a custom bundle from the current project using the comma seperated +list of packs as the packs to include in the bundle. + +Note this will compile all of the queries and may take a long time. In this case, you may create a bundle as follows: + +``` +qlt codeql install --quick-bundle [--packs pack1 pack2 pack3] +``` + +Which will not compile the packs and queries. + +In addition to specifying one of the bundle flags, you must set `ExportedCustomizationPacks` in `qlt.conf.json`. +If the `--pack` option is not specified on the command line, this is the value that is used for selecting which packs +to include in the bundle. + +Note that the versions of what gets installed is taken from `qlt.conf.json`. -Note that `QLT_CODEQL_PATH` being set overrides all resolution attempted by QLT. +For a normal installation, the mapping of these values is as follows: -If `QLT_CODEQL_PATH` is *not* set, then QLT will attempt to resolve the required version of CodeQL by looking for the -`qlt.conf.json` variable `CodeQLStandardLibraryIdent`, which is automatically set by QLT when the version of CodeQL is set. +- `CodeQLCLI` - The version of the CLI binaries to use +- `CodeQLStandardLibrary` - the version of the standard library to use. + +For a bundle installation the mapping is as follows: + +- `CodeQLCLIBundle` - The bundle downloaded from `github/codeql-action/releases` to base the bundle on. + +In all cases, at the end of the execution two environment variables are set: +- `QLT_CODEQL_PATH` - The path to the CodeQL binary. +- `QLT_CODEQL_HOME` - The root installation of CodeQL + +## Idents within the Installation Directory + +The layout of the installation repository is as follows: + +``` +.qlt/repo/packages/ident/codeql <-- normal CodeQL installs +.qlt/repo/custom-bundle/ident/codeql <-- custom bundles +``` + +For the case of package installations, the ident is generated as follows: + +```C# + var ident = String.Join("", "codeql-cli-" + CLIVersion, "#standard-library-ident-" ,StandardLibraryIdent); + return StringUtils.CreateMD5(FileUtils.SanitizeFilename(ident)).ToLower(); +``` -Note that for custom bundles, the variable `EnableCustomCodeQLBundles` controls if custom bundles will be used. In general, -when using QLT a previously installed version of CodeQL may easily be detected in the repository by looking for the ident. -However, since there is no ident for a custom bundle, doing a `qlt codeql install` is always necessary prior to invoking the other -functions of QLT. +Otherwise the ident is generated as follows: -For cases where one simply wishes to use CodeQL without a custom bundle (MAD extensions, queries, etc) then QLT will attempt to resolve -the correct version of CodeQL by using the `CodeQLStandardLibraryIdent`. If this cannot be resolved, QLT will notify the user that -one must first run `qlt codeql install` prior to proceeding. +```C# + var ident = String.Join("", "codeql-bundle-" + CLIBundle); + return StringUtils.CreateMD5(FileUtils.SanitizeFilename(ident)).ToLower(); +``` \ No newline at end of file diff --git a/src/CodeQLToolkit.Shared/Options/Globals.cs b/src/CodeQLToolkit.Shared/Options/Globals.cs index 4515ae1..8be4a03 100644 --- a/src/CodeQLToolkit.Shared/Options/Globals.cs +++ b/src/CodeQLToolkit.Shared/Options/Globals.cs @@ -25,5 +25,12 @@ public class Globals return false; }, "Turns on development mode which enables special features used in the development of QLT.") { IsRequired = true }.FromAmong(SupportedAutomationTypes); + + public static Option UseBundle { get; } = new Option("--use-bundle", () => { + return false; + }, "Switching QLT from using the distribution versions of CodeQL to using a Custom Bundle.") + { IsRequired = true }.FromAmong(SupportedAutomationTypes); + + } } diff --git a/src/CodeQLToolkit.Shared/Target/ITarget.cs b/src/CodeQLToolkit.Shared/Target/ITarget.cs index 54c0a23..16f0c26 100644 --- a/src/CodeQLToolkit.Shared/Target/ITarget.cs +++ b/src/CodeQLToolkit.Shared/Target/ITarget.cs @@ -10,6 +10,8 @@ namespace CodeQLToolkit.Shared.Target public abstract class ITarget { public string Base { get; set; } + + public bool UseBundle { get; set; } public abstract void Run(); public void DieWithError(string message) diff --git a/src/CodeQLToolkit.Shared/Utils/QLTConfig.cs b/src/CodeQLToolkit.Shared/Utils/QLTConfig.cs index 708a329..d660e72 100644 --- a/src/CodeQLToolkit.Shared/Utils/QLTConfig.cs +++ b/src/CodeQLToolkit.Shared/Utils/QLTConfig.cs @@ -14,8 +14,6 @@ public class QLTConfig public string CodeQLCLIBundle { get; set; } public string[] ExportedCustomizationPacks { get; set; } - - public bool EnableCustomCodeQLBundles { get; set; } public string CodeQLStandardLibraryIdent { get { From e062bea006be33fa439c34f76d830a79d9a5966b Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Wed, 21 Feb 2024 15:41:59 -0500 Subject: [PATCH 30/44] adding two new workflows for testing bundles --- .../internal-pr-test-bundle-creation.yml | 49 +++++++++++++++++++ ...internal-pr-test-quick-bundle-creation.yml | 49 +++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 .github/workflows/internal-pr-test-bundle-creation.yml create mode 100644 .github/workflows/internal-pr-test-quick-bundle-creation.yml diff --git a/.github/workflows/internal-pr-test-bundle-creation.yml b/.github/workflows/internal-pr-test-bundle-creation.yml new file mode 100644 index 0000000..fe75cae --- /dev/null +++ b/.github/workflows/internal-pr-test-bundle-creation.yml @@ -0,0 +1,49 @@ +name: ⚙️ Test Bundle Creation + +on: + push: + branches: + - 'main' + pull_request: + branches: + - 'main' + workflow_dispatch: + +jobs: + test-bundle-creation: + name: Test Bundle Creation + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install QLT + id: install-qlt + uses: ./.github/actions/install-qlt-local + with: + qlt-version: 'latest' + add-to-path: true + + - name: Validate QLT Installation + shell: bash + run: | + echo -e "Checking QLT Version:" + echo "QLT Home: ${{ steps.install-qlt.outputs.qlt-home }}" + qlt version + + - name: Create Bundle (compiled) + shell: bash + run: | + if ! qlt codeql run install --base example/ --custom-bundle ; then + echo "Failed to generate bundle." + exit 1 + fi + + # ensure bundle runs + + if ! qlt query run install-packs --base example/ ; then + echo "Failed to install query packs with tool." + exit 1 + fi + + diff --git a/.github/workflows/internal-pr-test-quick-bundle-creation.yml b/.github/workflows/internal-pr-test-quick-bundle-creation.yml new file mode 100644 index 0000000..b1d4f5e --- /dev/null +++ b/.github/workflows/internal-pr-test-quick-bundle-creation.yml @@ -0,0 +1,49 @@ +name: ⚙️ Test Bundle Creation (Quick) + +on: + push: + branches: + - 'main' + pull_request: + branches: + - 'main' + workflow_dispatch: + +jobs: + test-bundle-creation: + name: Test Bundle Creation + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install QLT + id: install-qlt + uses: ./.github/actions/install-qlt-local + with: + qlt-version: 'latest' + add-to-path: true + + - name: Validate QLT Installation + shell: bash + run: | + echo -e "Checking QLT Version:" + echo "QLT Home: ${{ steps.install-qlt.outputs.qlt-home }}" + qlt version + + - name: Create Bundle (quick) + shell: bash + run: | + if ! qlt codeql run install --base example/ --quick-bundle ; then + echo "Failed to generate bundle." + exit 1 + fi + + # ensure bundle runs + + if ! qlt query run install-packs --base example/ ; then + echo "Failed to install query packs with tool." + exit 1 + fi + + From 76862facc80c0faf5f7fd6f52c6c4ceef13467da Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Wed, 21 Feb 2024 15:48:33 -0500 Subject: [PATCH 31/44] update local install action so that the binary tools get created --- .github/actions/install-qlt-local/action.yml | 25 +++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/.github/actions/install-qlt-local/action.yml b/.github/actions/install-qlt-local/action.yml index ae6dd29..2c0d61a 100644 --- a/.github/actions/install-qlt-local/action.yml +++ b/.github/actions/install-qlt-local/action.yml @@ -47,13 +47,36 @@ runs: # repair permissions chmod +x ./src/CodeQLToolkit.Core/bin/Release/net6.0/publish/linux-x64/qlt chmod +r -R ./src/CodeQLToolkit.Core/bin/Release/net6.0/publish/linux-x64 - + + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Build CodeQL Bundle Tool for Packaging + shell: pwsh + run: | + # need this for the bundling to work. + pip install poetry + pip install -U pyinstaller + + # run the packaging + ./scripts/build_codeql_bundle_dist.ps1 -Version 0.2.0 -WorkDirectory dist -DestinationDirectory ./src/CodeQLToolkit.Core/bin/Release/net6.0/publish/linux-x64/tools/ + env: + GH_TOKEN: ${{ github.token }} + + - name: Build Bundle Archive + shell: bash + run: | + echo "Current Directory $(pwd)" + # create bundle ARCHIVE="$(pwd)/qlt-linux-x86_64.zip" pushd ./src/CodeQLToolkit.Core/bin/Release/net6.0/publish/linux-x64 zip -r $ARCHIVE . popd + + - name: Move Artifacts shell: pwsh run: | From 84e02c8c6549eb163fb60611de3e0efe3bf484b4 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Wed, 21 Feb 2024 15:58:05 -0500 Subject: [PATCH 32/44] adding custom bundle flag --- .github/workflows/internal-pr-test-bundle-creation.yml | 2 +- .github/workflows/internal-pr-test-quick-bundle-creation.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/internal-pr-test-bundle-creation.yml b/.github/workflows/internal-pr-test-bundle-creation.yml index fe75cae..e8d7b20 100644 --- a/.github/workflows/internal-pr-test-bundle-creation.yml +++ b/.github/workflows/internal-pr-test-bundle-creation.yml @@ -41,7 +41,7 @@ jobs: # ensure bundle runs - if ! qlt query run install-packs --base example/ ; then + if ! qlt query run install-packs --use-bundle --base example/ ; then echo "Failed to install query packs with tool." exit 1 fi diff --git a/.github/workflows/internal-pr-test-quick-bundle-creation.yml b/.github/workflows/internal-pr-test-quick-bundle-creation.yml index b1d4f5e..34e81cb 100644 --- a/.github/workflows/internal-pr-test-quick-bundle-creation.yml +++ b/.github/workflows/internal-pr-test-quick-bundle-creation.yml @@ -41,7 +41,7 @@ jobs: # ensure bundle runs - if ! qlt query run install-packs --base example/ ; then + if ! qlt query run install-packs --use-bundle --base example/ ; then echo "Failed to install query packs with tool." exit 1 fi From d883c1c8f8e7e9e311a24016811e0378965ac9d0 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Thu, 22 Feb 2024 14:08:48 -0500 Subject: [PATCH 33/44] adding bundle tests --- .../customizations/src/codeql-pack.lock.yml | 18 ++++++++++++++++++ example/cpp/customizations/src/qlpack.yml | 5 +++++ .../customizations/src/qlt/Customizations.qll | 13 +++++++++++++ .../customizations/test/codeql-pack.lock.yml | 18 ++++++++++++++++++ .../FooExternalSourceFunction/Foo.expected | 1 + .../FooExternalSourceFunction/Foo.ql | 5 +++++ .../FooExternalSourceFunction/test.c | 7 +++++++ .../FooExternalSourceFunction/Foo.expected | 0 .../FooExternalSourceFunction/Foo.ql | 4 ++++ .../FooExternalSourceFunction/test.c | 7 +++++++ example/cpp/customizations/test/qlpack.yml | 6 ++++++ example/qlt.conf.json | 2 +- scratch/.gitignore | 1 + 13 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 example/cpp/customizations/src/codeql-pack.lock.yml create mode 100644 example/cpp/customizations/src/qlpack.yml create mode 100644 example/cpp/customizations/src/qlt/Customizations.qll create mode 100644 example/cpp/customizations/test/codeql-pack.lock.yml create mode 100644 example/cpp/customizations/test/post-bundle/FooExternalSourceFunction/Foo.expected create mode 100644 example/cpp/customizations/test/post-bundle/FooExternalSourceFunction/Foo.ql create mode 100644 example/cpp/customizations/test/post-bundle/FooExternalSourceFunction/test.c create mode 100644 example/cpp/customizations/test/pre-bundle/FooExternalSourceFunction/Foo.expected create mode 100644 example/cpp/customizations/test/pre-bundle/FooExternalSourceFunction/Foo.ql create mode 100644 example/cpp/customizations/test/pre-bundle/FooExternalSourceFunction/test.c create mode 100644 example/cpp/customizations/test/qlpack.yml create mode 100644 scratch/.gitignore diff --git a/example/cpp/customizations/src/codeql-pack.lock.yml b/example/cpp/customizations/src/codeql-pack.lock.yml new file mode 100644 index 0000000..4edf97c --- /dev/null +++ b/example/cpp/customizations/src/codeql-pack.lock.yml @@ -0,0 +1,18 @@ +--- +lockVersion: 1.0.0 +dependencies: + codeql/cpp-all: + version: 0.12.2 + codeql/dataflow: + version: 0.1.5 + codeql/rangeanalysis: + version: 0.0.4 + codeql/ssa: + version: 0.2.5 + codeql/tutorial: + version: 0.2.5 + codeql/typetracking: + version: 0.2.5 + codeql/util: + version: 0.2.5 +compiled: false diff --git a/example/cpp/customizations/src/qlpack.yml b/example/cpp/customizations/src/qlpack.yml new file mode 100644 index 0000000..06c5c2a --- /dev/null +++ b/example/cpp/customizations/src/qlpack.yml @@ -0,0 +1,5 @@ +library: true +name: qlt/cpp-customizations +version: 0.0.1 +dependencies: + "codeql/cpp-all": "0.12.2" \ No newline at end of file diff --git a/example/cpp/customizations/src/qlt/Customizations.qll b/example/cpp/customizations/src/qlt/Customizations.qll new file mode 100644 index 0000000..60425ab --- /dev/null +++ b/example/cpp/customizations/src/qlt/Customizations.qll @@ -0,0 +1,13 @@ +import cpp +private import semmle.code.cpp.security.FlowSources + +module FooSources { + private class FooExternalSourceFunction extends RemoteFlowSourceFunction { + FooExternalSourceFunction() { this.hasName("foo") } + + override predicate hasRemoteFlowSource(FunctionOutput output, string description) { + output.isReturnValue() and + description = "value returned by " + this.getName() + } + } +} diff --git a/example/cpp/customizations/test/codeql-pack.lock.yml b/example/cpp/customizations/test/codeql-pack.lock.yml new file mode 100644 index 0000000..4edf97c --- /dev/null +++ b/example/cpp/customizations/test/codeql-pack.lock.yml @@ -0,0 +1,18 @@ +--- +lockVersion: 1.0.0 +dependencies: + codeql/cpp-all: + version: 0.12.2 + codeql/dataflow: + version: 0.1.5 + codeql/rangeanalysis: + version: 0.0.4 + codeql/ssa: + version: 0.2.5 + codeql/tutorial: + version: 0.2.5 + codeql/typetracking: + version: 0.2.5 + codeql/util: + version: 0.2.5 +compiled: false diff --git a/example/cpp/customizations/test/post-bundle/FooExternalSourceFunction/Foo.expected b/example/cpp/customizations/test/post-bundle/FooExternalSourceFunction/Foo.expected new file mode 100644 index 0000000..190f12b --- /dev/null +++ b/example/cpp/customizations/test/post-bundle/FooExternalSourceFunction/Foo.expected @@ -0,0 +1 @@ +| test.c:4:13:4:15 | call to foo | diff --git a/example/cpp/customizations/test/post-bundle/FooExternalSourceFunction/Foo.ql b/example/cpp/customizations/test/post-bundle/FooExternalSourceFunction/Foo.ql new file mode 100644 index 0000000..43c8c08 --- /dev/null +++ b/example/cpp/customizations/test/post-bundle/FooExternalSourceFunction/Foo.ql @@ -0,0 +1,5 @@ +import cpp +import qlt.Customizations +import semmle.code.cpp.security.FlowSources + +select any(RemoteFlowSource s) \ No newline at end of file diff --git a/example/cpp/customizations/test/post-bundle/FooExternalSourceFunction/test.c b/example/cpp/customizations/test/post-bundle/FooExternalSourceFunction/test.c new file mode 100644 index 0000000..95255a6 --- /dev/null +++ b/example/cpp/customizations/test/post-bundle/FooExternalSourceFunction/test.c @@ -0,0 +1,7 @@ +int foo(); + +int main(int argc, char** argv) { + int i = foo(); + + return i; +} \ No newline at end of file diff --git a/example/cpp/customizations/test/pre-bundle/FooExternalSourceFunction/Foo.expected b/example/cpp/customizations/test/pre-bundle/FooExternalSourceFunction/Foo.expected new file mode 100644 index 0000000..e69de29 diff --git a/example/cpp/customizations/test/pre-bundle/FooExternalSourceFunction/Foo.ql b/example/cpp/customizations/test/pre-bundle/FooExternalSourceFunction/Foo.ql new file mode 100644 index 0000000..3746afe --- /dev/null +++ b/example/cpp/customizations/test/pre-bundle/FooExternalSourceFunction/Foo.ql @@ -0,0 +1,4 @@ +import cpp +import semmle.code.cpp.security.FlowSources + +select any(RemoteFlowSource s) \ No newline at end of file diff --git a/example/cpp/customizations/test/pre-bundle/FooExternalSourceFunction/test.c b/example/cpp/customizations/test/pre-bundle/FooExternalSourceFunction/test.c new file mode 100644 index 0000000..95255a6 --- /dev/null +++ b/example/cpp/customizations/test/pre-bundle/FooExternalSourceFunction/test.c @@ -0,0 +1,7 @@ +int foo(); + +int main(int argc, char** argv) { + int i = foo(); + + return i; +} \ No newline at end of file diff --git a/example/cpp/customizations/test/qlpack.yml b/example/cpp/customizations/test/qlpack.yml new file mode 100644 index 0000000..f4e5b27 --- /dev/null +++ b/example/cpp/customizations/test/qlpack.yml @@ -0,0 +1,6 @@ +library: true +name: qlt/cpp-customizations-tests +version: 0.0.1 +dependencies: + "qlt/cpp-customizations": "*" +extractor: cpp \ No newline at end of file diff --git a/example/qlt.conf.json b/example/qlt.conf.json index 2379f33..26be63f 100644 --- a/example/qlt.conf.json +++ b/example/qlt.conf.json @@ -5,6 +5,6 @@ "EnableCustomCodeQLBundles": true, "CodeQLStandardLibraryIdent": "codeql-cli_v2.15.5", "ExportedCustomizationPacks" : [ - "qlt2/stuff2" + "qlt/cpp-customizations" ] } \ No newline at end of file diff --git a/scratch/.gitignore b/scratch/.gitignore new file mode 100644 index 0000000..1b473bd --- /dev/null +++ b/scratch/.gitignore @@ -0,0 +1 @@ +/codeql-bundle-win64.tar.gz From ecfd03acd3848656fbbc0caf33e53584511374ff Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Thu, 22 Feb 2024 15:47:07 -0500 Subject: [PATCH 34/44] integration tests --- ...nternal-pr-bundle-integration-test-cpp.yml | 85 +++++++++++++++++++ integration/cpp/expected.sarif | 0 integration/cpp/src/main.c | 7 ++ 3 files changed, 92 insertions(+) create mode 100644 .github/workflows/internal-pr-bundle-integration-test-cpp.yml create mode 100644 integration/cpp/expected.sarif create mode 100644 integration/cpp/src/main.c diff --git a/.github/workflows/internal-pr-bundle-integration-test-cpp.yml b/.github/workflows/internal-pr-bundle-integration-test-cpp.yml new file mode 100644 index 0000000..7ae94f2 --- /dev/null +++ b/.github/workflows/internal-pr-bundle-integration-test-cpp.yml @@ -0,0 +1,85 @@ +name: ⚙️ Integration Test Bundle on PR (CPP) + +on: + push: + branches: + - 'main' + pull_request: + branches: + - 'main' + workflow_dispatch: + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + strategy: + fail-fast: false + matrix: + language: [ 'cpp' ] + steps: + - name: Checkout repository + uses: actions/checkout@4 + + - name: Install QLT + id: install-qlt + uses: ./.github/actions/install-qlt-local + with: + qlt-version: 'latest' + add-to-path: true + + - name: Validate QLT Installation + shell: bash + run: | + echo -e "Checking QLT Version:" + echo "QLT Home: ${{ steps.install-qlt.outputs.qlt-home }}" + qlt version + + - name: Create Bundle (compiled) + shell: bash + run: | + if ! qlt codeql run install --base example/ --custom-bundle ; then + echo "Failed to generate bundle." + exit 1 + fi + + # ensure bundle runs + + if ! qlt query run install-packs --use-bundle --base example/ ; then + echo "Failed to install query packs with tool." + exit 1 + fi + + - name: Validate Bundle Existence + shell: bash + run: | + echo "Checking Bundle Existence" + ls -l ${{ env.QLT_CODEQL_HOME }}/../out/ + + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + queries: security-extended + source-root: integration/cpp/src/ # Path containing the example application + tools: ${{ env.QLT_CODEQL_HOME }}/../out/codeql-bundle.tar.gz + + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + with: + working-directory: integration/cpp/src/ # Path containing the example application + + - name: Perform CodeQL Analysis + id: analysis + uses: github/codeql-action/analyze@v2 + + - name: Validate SARIF Location + shell: bash + run: | + # validate we have the actual sarif results + echo "Checking SARIF file location at: ${{ steps.analysis.outputs.sarif-output }}" + ls -l ${{ steps.analysis.outputs.sarif-output }} \ No newline at end of file diff --git a/integration/cpp/expected.sarif b/integration/cpp/expected.sarif new file mode 100644 index 0000000..e69de29 diff --git a/integration/cpp/src/main.c b/integration/cpp/src/main.c new file mode 100644 index 0000000..95255a6 --- /dev/null +++ b/integration/cpp/src/main.c @@ -0,0 +1,7 @@ +int foo(); + +int main(int argc, char** argv) { + int i = foo(); + + return i; +} \ No newline at end of file From e1b4f1b80b1f6a73aadf73dee82f25b9d9dee73d Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Thu, 22 Feb 2024 15:50:13 -0500 Subject: [PATCH 35/44] syntax --- ...nternal-pr-bundle-integration-test-cpp.yml | 59 ++++++++++--------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/.github/workflows/internal-pr-bundle-integration-test-cpp.yml b/.github/workflows/internal-pr-bundle-integration-test-cpp.yml index 7ae94f2..d165536 100644 --- a/.github/workflows/internal-pr-bundle-integration-test-cpp.yml +++ b/.github/workflows/internal-pr-bundle-integration-test-cpp.yml @@ -21,44 +21,45 @@ jobs: fail-fast: false matrix: language: [ 'cpp' ] + steps: - name: Checkout repository uses: actions/checkout@4 - - name: Install QLT - id: install-qlt - uses: ./.github/actions/install-qlt-local - with: - qlt-version: 'latest' - add-to-path: true + - name: Install QLT + id: install-qlt + uses: ./.github/actions/install-qlt-local + with: + qlt-version: 'latest' + add-to-path: true - - name: Validate QLT Installation - shell: bash - run: | - echo -e "Checking QLT Version:" - echo "QLT Home: ${{ steps.install-qlt.outputs.qlt-home }}" - qlt version + - name: Validate QLT Installation + shell: bash + run: | + echo -e "Checking QLT Version:" + echo "QLT Home: ${{ steps.install-qlt.outputs.qlt-home }}" + qlt version - - name: Create Bundle (compiled) - shell: bash - run: | - if ! qlt codeql run install --base example/ --custom-bundle ; then - echo "Failed to generate bundle." - exit 1 - fi + - name: Create Bundle (compiled) + shell: bash + run: | + if ! qlt codeql run install --base example/ --custom-bundle ; then + echo "Failed to generate bundle." + exit 1 + fi - # ensure bundle runs + # ensure bundle runs - if ! qlt query run install-packs --use-bundle --base example/ ; then - echo "Failed to install query packs with tool." - exit 1 - fi + if ! qlt query run install-packs --use-bundle --base example/ ; then + echo "Failed to install query packs with tool." + exit 1 + fi - - name: Validate Bundle Existence - shell: bash - run: | - echo "Checking Bundle Existence" - ls -l ${{ env.QLT_CODEQL_HOME }}/../out/ + - name: Validate Bundle Existence + shell: bash + run: | + echo "Checking Bundle Existence" + ls -l ${{ env.QLT_CODEQL_HOME }}/../out/ - name: Initialize CodeQL uses: github/codeql-action/init@v2 From ddb64c31607e4e88dc7757d30842669f53adc3cd Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Thu, 22 Feb 2024 15:51:58 -0500 Subject: [PATCH 36/44] version 4 --- .github/workflows/internal-pr-bundle-integration-test-cpp.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/internal-pr-bundle-integration-test-cpp.yml b/.github/workflows/internal-pr-bundle-integration-test-cpp.yml index d165536..b466cd6 100644 --- a/.github/workflows/internal-pr-bundle-integration-test-cpp.yml +++ b/.github/workflows/internal-pr-bundle-integration-test-cpp.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@4 + uses: actions/checkout@v4 - name: Install QLT id: install-qlt From 42291ffa42c1ca3e310a8da93d024237290f953f Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Thu, 22 Feb 2024 16:27:33 -0500 Subject: [PATCH 37/44] adding builder --- integration/cpp/src/Makefile | 8 ++++++++ integration/cpp/src/main.c | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 integration/cpp/src/Makefile diff --git a/integration/cpp/src/Makefile b/integration/cpp/src/Makefile new file mode 100644 index 0000000..111a6e9 --- /dev/null +++ b/integration/cpp/src/Makefile @@ -0,0 +1,8 @@ +all : main + +a : main.o + +clean : + rm -f main main.o + +.PHONY: all clean diff --git a/integration/cpp/src/main.c b/integration/cpp/src/main.c index 95255a6..6dc2ee6 100644 --- a/integration/cpp/src/main.c +++ b/integration/cpp/src/main.c @@ -1,4 +1,4 @@ -int foo(); +int foo(){} int main(int argc, char** argv) { int i = foo(); From 15b304fb5583d26bc557f21c41cadaf71b0abedd Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Thu, 22 Feb 2024 16:41:29 -0500 Subject: [PATCH 38/44] bits to make sarif comparison work --- ...nternal-pr-bundle-integration-test-cpp.yml | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/.github/workflows/internal-pr-bundle-integration-test-cpp.yml b/.github/workflows/internal-pr-bundle-integration-test-cpp.yml index b466cd6..67de39f 100644 --- a/.github/workflows/internal-pr-bundle-integration-test-cpp.yml +++ b/.github/workflows/internal-pr-bundle-integration-test-cpp.yml @@ -83,4 +83,23 @@ jobs: run: | # validate we have the actual sarif results echo "Checking SARIF file location at: ${{ steps.analysis.outputs.sarif-output }}" - ls -l ${{ steps.analysis.outputs.sarif-output }} \ No newline at end of file + ls -l ${{ steps.analysis.outputs.sarif-output }} + + - name: Upload SARIF Results + uses: actions/upload-artifact@v2 + with: + name: actual.sarif + path: | + ${{ steps.analysis.outputs.sarif-output }}/*.sarif + if-no-files-found: error + + + - name: Validate SARIF Results + shell: bash + run: | + # Compare the expected vs the actual + + if ! diff integration/cpp/expected.sarif ${{ steps.analysis.outputs.sarif-output }}/cpp.sarif ; then + echo "Expected file does not match actual. Please check the SARIF file for differences." + exit 1 + fi From 8f1cb72a504677d827f07521cc8f3bd57cdab21a Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Thu, 22 Feb 2024 16:58:29 -0500 Subject: [PATCH 39/44] adding expected --- integration/cpp/expected.sarif | 2427 ++++++++++++++++++++++++++++++++ 1 file changed, 2427 insertions(+) diff --git a/integration/cpp/expected.sarif b/integration/cpp/expected.sarif index e69de29..dc75610 100644 --- a/integration/cpp/expected.sarif +++ b/integration/cpp/expected.sarif @@ -0,0 +1,2427 @@ +{ + "$schema" : "https://json.schemastore.org/sarif-2.1.0.json", + "version" : "2.1.0", + "runs" : [ { + "tool" : { + "driver" : { + "name" : "CodeQL", + "organization" : "GitHub", + "semanticVersion" : "2.15.5", + "notifications" : [ { + "id" : "cli/expected-extracted-files/c", + "name" : "cli/expected-extracted-files/c", + "shortDescription" : { + "text" : "Expected extracted files" + }, + "fullDescription" : { + "text" : "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration" : { + "enabled" : true + }, + "properties" : { + "tags" : [ "expected-extracted-files", "telemetry" ], + "languageDisplayName" : "C" + } + }, { + "id" : "cpp/autobuilder/deptrace", + "name" : "cpp/autobuilder/deptrace", + "shortDescription" : { + "text" : "deptrace was enabled" + }, + "fullDescription" : { + "text" : "deptrace was enabled" + }, + "defaultConfiguration" : { + "enabled" : true + } + } ], + "rules" : [ ] + }, + "extensions" : [ { + "name" : "codeql/cpp-queries", + "semanticVersion" : "0.9.1+d540fc0794dcb2a6c10648b8925403788612e976", + "notifications" : [ { + "id" : "cpp/diagnostics/extraction-warnings", + "name" : "cpp/diagnostics/extraction-warnings", + "shortDescription" : { + "text" : "Extraction warnings" + }, + "fullDescription" : { + "text" : "List all extraction warnings for files in the source code directory." + }, + "defaultConfiguration" : { + "enabled" : true + }, + "properties" : { + "description" : "List all extraction warnings for files in the source code directory.", + "id" : "cpp/diagnostics/extraction-warnings", + "kind" : "diagnostic", + "name" : "Extraction warnings" + } + }, { + "id" : "cpp/diagnostics/successfully-extracted-files", + "name" : "cpp/diagnostics/successfully-extracted-files", + "shortDescription" : { + "text" : "Successfully extracted files" + }, + "fullDescription" : { + "text" : "Lists all files in the source code directory that were extracted without encountering a problem in the file." + }, + "defaultConfiguration" : { + "enabled" : true + }, + "properties" : { + "tags" : [ "successfully-extracted-files" ], + "description" : "Lists all files in the source code directory that were extracted without encountering a problem in the file.", + "id" : "cpp/diagnostics/successfully-extracted-files", + "kind" : "diagnostic", + "name" : "Successfully extracted files" + } + }, { + "id" : "cpp/diagnostics/failed-extractor-invocations", + "name" : "cpp/diagnostics/failed-extractor-invocations", + "shortDescription" : { + "text" : "Failed extractor invocations" + }, + "fullDescription" : { + "text" : "Gives the command line of compilations for which extraction did not run to completion." + }, + "defaultConfiguration" : { + "enabled" : true + }, + "properties" : { + "description" : "Gives the command line of compilations for which extraction did not run to completion.", + "id" : "cpp/diagnostics/failed-extractor-invocations", + "kind" : "diagnostic", + "name" : "Failed extractor invocations" + } + } ], + "rules" : [ { + "id" : "cpp/non-constant-format", + "name" : "cpp/non-constant-format", + "shortDescription" : { + "text" : "Non-constant format string" + }, + "fullDescription" : { + "text" : "Passing a non-constant 'format' string to a printf-like function can lead to a mismatch between the number of arguments defined by the 'format' and the number of arguments actually passed to the function. If the format string ultimately stems from an untrusted source, this can be used for exploits." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "note" + }, + "help" : { + "text" : "# Non-constant format string\nThe `printf` function, related functions like `sprintf` and `fprintf`, and other functions built atop `vprintf` all accept a format string as one of their arguments. When such format strings are literal constants, it is easy for the programmer (and static analysis tools) to verify that the format specifiers (such as `%s` and `%02x`) in the format string are compatible with the trailing arguments of the function call. When such format strings are not literal constants, it is more difficult to maintain the program: programmers (and static analysis tools) must perform non-local data-flow analysis to deduce what values the format string argument might take.\n\n\n## Recommendation\nIf the argument passed as a format string is meant to be a plain string rather than a format string, then pass `%s` as the format string, and pass the original argument as the sole trailing argument.\n\nIf the argument passed as a format string is a parameter to the enclosing function, then consider redesigning the enclosing function's API to be less brittle.\n\n\n## Example\nThe following program is meant to echo its command line arguments:\n\n\n```c\n#include \nint main(int argc, char** argv) {\n for(int i = 1; i < argc; ++i) {\n printf(argv[i]);\n }\n}\n```\nThe above program behaves as expected in most cases, but breaks when one of its command line arguments contains a percent character. In such cases, the behavior of the program is undefined: it might echo garbage, it might crash, or it might give a malicious attacker root access. One way of addressing the problem is to use a constant `%s` format string, as in the following program:\n\n\n```c\n#include \nint main(int argc, char** argv) {\n for(int i = 1; i < argc; ++i) {\n printf(\"%s\", argv[i]);\n }\n}\n```\n\n## Example\nThe following program defines a `log_with_timestamp` function:\n\n\n```c\nvoid log_with_timestamp(const char* message) {\n struct tm now;\n time(&now);\n printf(\"[%s] \", asctime(now));\n printf(message);\n}\n\nint main(int argc, char** argv) {\n log_with_timestamp(\"Application is starting...\\n\");\n /* ... */\n log_with_timestamp(\"Application is closing...\\n\");\n return 0;\n}\n```\nIn the code that is visible, the reader can verify that `log_with_timestamp` is never called with a log message containing a percent character, but even if all current calls are correct, this presents an ongoing maintenance burden to ensure that newly-introduced calls don't contain percent characters. As in the previous example, one solution is to make the log message a trailing argument of the function call:\n\n\n```c\nvoid log_with_timestamp(const char* message) {\n struct tm now;\n time(&now);\n printf(\"[%s] %s\", asctime(now), message);\n}\n\nint main(int argc, char** argv) {\n log_with_timestamp(\"Application is starting...\\n\");\n /* ... */\n log_with_timestamp(\"Application is closing...\\n\");\n return 0;\n}\n```\nAn alternative solution is to allow `log_with_timestamp` to accept format arguments:\n\n\n```c\nvoid log_with_timestamp(const char* message, ...) {\n va_list args;\n va_start(args, message);\n struct tm now;\n time(&now);\n printf(\"[%s] \", asctime(now));\n vprintf(message, args);\n va_end(args);\n}\n\nint main(int argc, char** argv) {\n log_with_timestamp(\"%s is starting...\\n\", argv[0]);\n /* ... */\n log_with_timestamp(\"%s is closing...\\n\", argv[0]);\n return 0;\n}\n```\nIn this formulation, the non-constant format string to `printf` has been replaced with a non-constant format string to `vprintf`. Semmle will no longer consider the body of `log_with_timestamp` to be a problem, and will instead check that every call to `log_with_timestamp` passes a constant format string.\n\n\n## References\n* CERT C Coding Standard: [FIO30-C. Exclude user input from format strings](https://www.securecoding.cert.org/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings).\n* M. Howard, D. Leblanc, J. Viega, *19 Deadly Sins of Software Security: Programming Flaws and How to Fix Them*.\n* Common Weakness Enumeration: [CWE-134](https://cwe.mitre.org/data/definitions/134.html).\n", + "markdown" : "# Non-constant format string\nThe `printf` function, related functions like `sprintf` and `fprintf`, and other functions built atop `vprintf` all accept a format string as one of their arguments. When such format strings are literal constants, it is easy for the programmer (and static analysis tools) to verify that the format specifiers (such as `%s` and `%02x`) in the format string are compatible with the trailing arguments of the function call. When such format strings are not literal constants, it is more difficult to maintain the program: programmers (and static analysis tools) must perform non-local data-flow analysis to deduce what values the format string argument might take.\n\n\n## Recommendation\nIf the argument passed as a format string is meant to be a plain string rather than a format string, then pass `%s` as the format string, and pass the original argument as the sole trailing argument.\n\nIf the argument passed as a format string is a parameter to the enclosing function, then consider redesigning the enclosing function's API to be less brittle.\n\n\n## Example\nThe following program is meant to echo its command line arguments:\n\n\n```c\n#include \nint main(int argc, char** argv) {\n for(int i = 1; i < argc; ++i) {\n printf(argv[i]);\n }\n}\n```\nThe above program behaves as expected in most cases, but breaks when one of its command line arguments contains a percent character. In such cases, the behavior of the program is undefined: it might echo garbage, it might crash, or it might give a malicious attacker root access. One way of addressing the problem is to use a constant `%s` format string, as in the following program:\n\n\n```c\n#include \nint main(int argc, char** argv) {\n for(int i = 1; i < argc; ++i) {\n printf(\"%s\", argv[i]);\n }\n}\n```\n\n## Example\nThe following program defines a `log_with_timestamp` function:\n\n\n```c\nvoid log_with_timestamp(const char* message) {\n struct tm now;\n time(&now);\n printf(\"[%s] \", asctime(now));\n printf(message);\n}\n\nint main(int argc, char** argv) {\n log_with_timestamp(\"Application is starting...\\n\");\n /* ... */\n log_with_timestamp(\"Application is closing...\\n\");\n return 0;\n}\n```\nIn the code that is visible, the reader can verify that `log_with_timestamp` is never called with a log message containing a percent character, but even if all current calls are correct, this presents an ongoing maintenance burden to ensure that newly-introduced calls don't contain percent characters. As in the previous example, one solution is to make the log message a trailing argument of the function call:\n\n\n```c\nvoid log_with_timestamp(const char* message) {\n struct tm now;\n time(&now);\n printf(\"[%s] %s\", asctime(now), message);\n}\n\nint main(int argc, char** argv) {\n log_with_timestamp(\"Application is starting...\\n\");\n /* ... */\n log_with_timestamp(\"Application is closing...\\n\");\n return 0;\n}\n```\nAn alternative solution is to allow `log_with_timestamp` to accept format arguments:\n\n\n```c\nvoid log_with_timestamp(const char* message, ...) {\n va_list args;\n va_start(args, message);\n struct tm now;\n time(&now);\n printf(\"[%s] \", asctime(now));\n vprintf(message, args);\n va_end(args);\n}\n\nint main(int argc, char** argv) {\n log_with_timestamp(\"%s is starting...\\n\", argv[0]);\n /* ... */\n log_with_timestamp(\"%s is closing...\\n\", argv[0]);\n return 0;\n}\n```\nIn this formulation, the non-constant format string to `printf` has been replaced with a non-constant format string to `vprintf`. Semmle will no longer consider the body of `log_with_timestamp` to be a problem, and will instead check that every call to `log_with_timestamp` passes a constant format string.\n\n\n## References\n* CERT C Coding Standard: [FIO30-C. Exclude user input from format strings](https://www.securecoding.cert.org/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings).\n* M. Howard, D. Leblanc, J. Viega, *19 Deadly Sins of Software Security: Programming Flaws and How to Fix Them*.\n* Common Weakness Enumeration: [CWE-134](https://cwe.mitre.org/data/definitions/134.html).\n" + }, + "properties" : { + "tags" : [ "maintainability", "correctness", "security", "external/cwe/cwe-134" ], + "description" : "Passing a non-constant 'format' string to a printf-like function can lead\n to a mismatch between the number of arguments defined by the 'format' and the number\n of arguments actually passed to the function. If the format string ultimately stems\n from an untrusted source, this can be used for exploits.", + "id" : "cpp/non-constant-format", + "kind" : "problem", + "name" : "Non-constant format string", + "precision" : "high", + "problem.severity" : "recommendation", + "security-severity" : "9.3" + } + }, { + "id" : "cpp/wrong-number-format-arguments", + "name" : "cpp/wrong-number-format-arguments", + "shortDescription" : { + "text" : "Too few arguments to formatting function" + }, + "fullDescription" : { + "text" : "Calling a printf-like function with too few arguments can be a source of security issues." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Too few arguments to formatting function\nEach call to the `printf` function, or a related function, should include the number of arguments defined by the format. Passing the function more arguments than required is harmless (although it may be indicative of other defects). However, passing the function fewer arguments than are defined by the format can be a security vulnerability since the function will process the next item on the stack as the missing arguments.\n\nThis might lead to an information leak if a sensitive value from the stack is printed. It might cause a crash if a value on the stack is interpreted as a pointer and leads to accessing unmapped memory. Finally, it may lead to a follow-on vulnerability if an attacker can use this problem to cause the output string to be too long or have unexpected contents.\n\n\n## Recommendation\nReview the format and arguments expected by the highlighted function calls. Update either the format or the arguments so that the expected number of arguments are passed to the function.\n\n\n## Example\n\n```cpp\nint main() {\n printf(\"%d, %s\\n\", 42); // Will crash or print garbage\n return 0;\n}\n\n```\n\n## References\n* CERT C Coding Standard: [FIO30-C. Exclude user input from format strings](https://www.securecoding.cert.org/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings).\n* cplusplus.com: [C++ Functions](http://www.tutorialspoint.com/cplusplus/cpp_functions.htm).\n* Microsoft C Runtime Library Reference: [printf, wprintf](https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/printf-printf-l-wprintf-wprintf-l).\n* Common Weakness Enumeration: [CWE-234](https://cwe.mitre.org/data/definitions/234.html).\n* Common Weakness Enumeration: [CWE-685](https://cwe.mitre.org/data/definitions/685.html).\n", + "markdown" : "# Too few arguments to formatting function\nEach call to the `printf` function, or a related function, should include the number of arguments defined by the format. Passing the function more arguments than required is harmless (although it may be indicative of other defects). However, passing the function fewer arguments than are defined by the format can be a security vulnerability since the function will process the next item on the stack as the missing arguments.\n\nThis might lead to an information leak if a sensitive value from the stack is printed. It might cause a crash if a value on the stack is interpreted as a pointer and leads to accessing unmapped memory. Finally, it may lead to a follow-on vulnerability if an attacker can use this problem to cause the output string to be too long or have unexpected contents.\n\n\n## Recommendation\nReview the format and arguments expected by the highlighted function calls. Update either the format or the arguments so that the expected number of arguments are passed to the function.\n\n\n## Example\n\n```cpp\nint main() {\n printf(\"%d, %s\\n\", 42); // Will crash or print garbage\n return 0;\n}\n\n```\n\n## References\n* CERT C Coding Standard: [FIO30-C. Exclude user input from format strings](https://www.securecoding.cert.org/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings).\n* cplusplus.com: [C++ Functions](http://www.tutorialspoint.com/cplusplus/cpp_functions.htm).\n* Microsoft C Runtime Library Reference: [printf, wprintf](https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/printf-printf-l-wprintf-wprintf-l).\n* Common Weakness Enumeration: [CWE-234](https://cwe.mitre.org/data/definitions/234.html).\n* Common Weakness Enumeration: [CWE-685](https://cwe.mitre.org/data/definitions/685.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "correctness", "security", "external/cwe/cwe-234", "external/cwe/cwe-685" ], + "description" : "Calling a printf-like function with too few arguments can be\n a source of security issues.", + "id" : "cpp/wrong-number-format-arguments", + "kind" : "problem", + "name" : "Too few arguments to formatting function", + "precision" : "high", + "problem.severity" : "error", + "security-severity" : "5.0" + } + }, { + "id" : "cpp/wrong-type-format-argument", + "name" : "cpp/wrong-type-format-argument", + "shortDescription" : { + "text" : "Wrong type of arguments to formatting function" + }, + "fullDescription" : { + "text" : "Calling a printf-like function with the wrong type of arguments causes unpredictable behavior." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Wrong type of arguments to formatting function\nEach call to the `printf` function or a related function should include the type and sequence of arguments defined by the format. If the function is passed arguments of a different type or in a different sequence then the arguments are reinterpreted to fit the type and sequence expected, resulting in unpredictable behavior.\n\n\n## Recommendation\nReview the format and arguments expected by the highlighted function calls. Update either the format or the arguments so that the expected type and sequence of arguments are passed to the function.\n\n\n## Example\n\n```cpp\nint main() {\n printf(\"%s\\n\", 42); //printf will treat 42 as a char*, will most likely segfault\n return 0;\n}\n\n```\n\n## References\n* CERT C Coding Standard: [FIO30-C. Exclude user input from format strings](https://www.securecoding.cert.org/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings).\n* cplusplus.com: [C++ Functions](http://www.tutorialspoint.com/cplusplus/cpp_functions.htm).\n* CRT Alphabetical Function Reference: [printf, _printf_l, wprintf, _wprintf_l](https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/printf-printf-l-wprintf-wprintf-l).\n* Common Weakness Enumeration: [CWE-686](https://cwe.mitre.org/data/definitions/686.html).\n", + "markdown" : "# Wrong type of arguments to formatting function\nEach call to the `printf` function or a related function should include the type and sequence of arguments defined by the format. If the function is passed arguments of a different type or in a different sequence then the arguments are reinterpreted to fit the type and sequence expected, resulting in unpredictable behavior.\n\n\n## Recommendation\nReview the format and arguments expected by the highlighted function calls. Update either the format or the arguments so that the expected type and sequence of arguments are passed to the function.\n\n\n## Example\n\n```cpp\nint main() {\n printf(\"%s\\n\", 42); //printf will treat 42 as a char*, will most likely segfault\n return 0;\n}\n\n```\n\n## References\n* CERT C Coding Standard: [FIO30-C. Exclude user input from format strings](https://www.securecoding.cert.org/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings).\n* cplusplus.com: [C++ Functions](http://www.tutorialspoint.com/cplusplus/cpp_functions.htm).\n* CRT Alphabetical Function Reference: [printf, _printf_l, wprintf, _wprintf_l](https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/printf-printf-l-wprintf-wprintf-l).\n* Common Weakness Enumeration: [CWE-686](https://cwe.mitre.org/data/definitions/686.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "correctness", "security", "external/cwe/cwe-686" ], + "description" : "Calling a printf-like function with the wrong type of arguments causes unpredictable\n behavior.", + "id" : "cpp/wrong-type-format-argument", + "kind" : "problem", + "name" : "Wrong type of arguments to formatting function", + "precision" : "high", + "problem.severity" : "error", + "security-severity" : "7.5" + } + }, { + "id" : "cpp/overflowing-snprintf", + "name" : "cpp/overflowing-snprintf", + "shortDescription" : { + "text" : "Potentially overflowing call to snprintf" + }, + "fullDescription" : { + "text" : "Using the return value from snprintf without proper checks can cause overflow." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Potentially overflowing call to snprintf\nThe return value of a call to `snprintf` is the number of characters that *would have* been written to the buffer assuming there was sufficient space. In the event that the operation reaches the end of the buffer and more than one character is discarded, the return value will be greater than the buffer size. This can cause incorrect behavior, for example:\n\n\n## Example\n\n```cpp\n#define BUF_SIZE (32)\n\nint main(int argc, char *argv[])\n{\n\tchar buffer[BUF_SIZE];\n\tsize_t pos = 0;\n\tint i;\n\n\tfor (i = 0; i < argc; i++)\n\t{\n\t\tpos += snprintf(buffer + pos, BUF_SIZE - pos, \"%s\", argv[i]);\n\t\t\t// BUF_SIZE - pos may overflow\n\t}\n}\n\n```\n\n## Recommendation\nThe return value of `snprintf` should always be checked if it is used, and values larger than the buffer size should be accounted for.\n\n\n## Example\n\n```cpp\n#define BUF_SIZE (32)\n\nint main(int argc, char *argv[])\n{\n\tchar buffer[BUF_SIZE];\n\tsize_t pos = 0;\n\tint i;\n\n\tfor (i = 0; i < argc; i++)\n\t{\n\t\tint n = snprintf(buffer + pos, BUF_SIZE - pos, \"%s\", argv[i]);\n\t\tif (n < 0 || n >= BUF_SIZE - pos)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t\tpos += n;\n\t}\n}\n\n```\n\n## References\n* cplusplus.com: [snprintf](http://www.cplusplus.com/reference/cstdio/snprintf/).\n* Red Hat Customer Portal: [The trouble with snprintf](https://access.redhat.com/blogs/766093/posts/1976193).\n* Common Weakness Enumeration: [CWE-190](https://cwe.mitre.org/data/definitions/190.html).\n* Common Weakness Enumeration: [CWE-253](https://cwe.mitre.org/data/definitions/253.html).\n", + "markdown" : "# Potentially overflowing call to snprintf\nThe return value of a call to `snprintf` is the number of characters that *would have* been written to the buffer assuming there was sufficient space. In the event that the operation reaches the end of the buffer and more than one character is discarded, the return value will be greater than the buffer size. This can cause incorrect behavior, for example:\n\n\n## Example\n\n```cpp\n#define BUF_SIZE (32)\n\nint main(int argc, char *argv[])\n{\n\tchar buffer[BUF_SIZE];\n\tsize_t pos = 0;\n\tint i;\n\n\tfor (i = 0; i < argc; i++)\n\t{\n\t\tpos += snprintf(buffer + pos, BUF_SIZE - pos, \"%s\", argv[i]);\n\t\t\t// BUF_SIZE - pos may overflow\n\t}\n}\n\n```\n\n## Recommendation\nThe return value of `snprintf` should always be checked if it is used, and values larger than the buffer size should be accounted for.\n\n\n## Example\n\n```cpp\n#define BUF_SIZE (32)\n\nint main(int argc, char *argv[])\n{\n\tchar buffer[BUF_SIZE];\n\tsize_t pos = 0;\n\tint i;\n\n\tfor (i = 0; i < argc; i++)\n\t{\n\t\tint n = snprintf(buffer + pos, BUF_SIZE - pos, \"%s\", argv[i]);\n\t\tif (n < 0 || n >= BUF_SIZE - pos)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t\tpos += n;\n\t}\n}\n\n```\n\n## References\n* cplusplus.com: [snprintf](http://www.cplusplus.com/reference/cstdio/snprintf/).\n* Red Hat Customer Portal: [The trouble with snprintf](https://access.redhat.com/blogs/766093/posts/1976193).\n* Common Weakness Enumeration: [CWE-190](https://cwe.mitre.org/data/definitions/190.html).\n* Common Weakness Enumeration: [CWE-253](https://cwe.mitre.org/data/definitions/253.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "correctness", "security", "external/cwe/cwe-190", "external/cwe/cwe-253" ], + "description" : "Using the return value from snprintf without proper checks can cause overflow.", + "id" : "cpp/overflowing-snprintf", + "kind" : "problem", + "name" : "Potentially overflowing call to snprintf", + "precision" : "high", + "problem.severity" : "warning", + "security-severity" : "8.1" + } + }, { + "id" : "cpp/integer-multiplication-cast-to-long", + "name" : "cpp/integer-multiplication-cast-to-long", + "shortDescription" : { + "text" : "Multiplication result converted to larger type" + }, + "fullDescription" : { + "text" : "A multiplication result that is converted to a larger type can be a sign that the result can overflow the type converted from." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Multiplication result converted to larger type\nThis rule finds code that converts the result of an integer multiplication to a larger type. Since the conversion applies *after* the multiplication, arithmetic overflow may still occur.\n\nThe rule flags every multiplication of two non-constant integer expressions that is (explicitly or implicitly) converted to a larger integer type. The conversion is an indication that the expression would produce a result that would be too large to fit in the smaller integer type.\n\n\n## Recommendation\nUse a cast to ensure that the multiplication is done using the larger integer type to avoid overflow.\n\n\n## Example\n\n```cpp\nint i = 2000000000;\nlong j = i * i; //Wrong: due to overflow on the multiplication between ints, \n //will result to j being -1651507200, not 4000000000000000000\n\nlong k = (long) i * i; //Correct: the multiplication is done on longs instead of ints, \n //and will not overflow\n\nlong l = static_cast(i) * i; //Correct: modern C++\n\n```\n\n## References\n* MSDN Library: [Multiplicative Operators and the Modulus Operator](https://docs.microsoft.com/en-us/cpp/cpp/multiplicative-operators-and-the-modulus-operator).\n* Cplusplus.com: [Integer overflow](http://www.cplusplus.com/articles/DE18T05o/).\n* Common Weakness Enumeration: [CWE-190](https://cwe.mitre.org/data/definitions/190.html).\n* Common Weakness Enumeration: [CWE-192](https://cwe.mitre.org/data/definitions/192.html).\n* Common Weakness Enumeration: [CWE-197](https://cwe.mitre.org/data/definitions/197.html).\n* Common Weakness Enumeration: [CWE-681](https://cwe.mitre.org/data/definitions/681.html).\n", + "markdown" : "# Multiplication result converted to larger type\nThis rule finds code that converts the result of an integer multiplication to a larger type. Since the conversion applies *after* the multiplication, arithmetic overflow may still occur.\n\nThe rule flags every multiplication of two non-constant integer expressions that is (explicitly or implicitly) converted to a larger integer type. The conversion is an indication that the expression would produce a result that would be too large to fit in the smaller integer type.\n\n\n## Recommendation\nUse a cast to ensure that the multiplication is done using the larger integer type to avoid overflow.\n\n\n## Example\n\n```cpp\nint i = 2000000000;\nlong j = i * i; //Wrong: due to overflow on the multiplication between ints, \n //will result to j being -1651507200, not 4000000000000000000\n\nlong k = (long) i * i; //Correct: the multiplication is done on longs instead of ints, \n //and will not overflow\n\nlong l = static_cast(i) * i; //Correct: modern C++\n\n```\n\n## References\n* MSDN Library: [Multiplicative Operators and the Modulus Operator](https://docs.microsoft.com/en-us/cpp/cpp/multiplicative-operators-and-the-modulus-operator).\n* Cplusplus.com: [Integer overflow](http://www.cplusplus.com/articles/DE18T05o/).\n* Common Weakness Enumeration: [CWE-190](https://cwe.mitre.org/data/definitions/190.html).\n* Common Weakness Enumeration: [CWE-192](https://cwe.mitre.org/data/definitions/192.html).\n* Common Weakness Enumeration: [CWE-197](https://cwe.mitre.org/data/definitions/197.html).\n* Common Weakness Enumeration: [CWE-681](https://cwe.mitre.org/data/definitions/681.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "security", "correctness", "types", "external/cwe/cwe-190", "external/cwe/cwe-192", "external/cwe/cwe-197", "external/cwe/cwe-681" ], + "description" : "A multiplication result that is converted to a larger type can\n be a sign that the result can overflow the type converted from.", + "id" : "cpp/integer-multiplication-cast-to-long", + "kind" : "problem", + "name" : "Multiplication result converted to larger type", + "precision" : "high", + "problem.severity" : "warning", + "security-severity" : "8.1" + } + }, { + "id" : "cpp/signed-overflow-check", + "name" : "cpp/signed-overflow-check", + "shortDescription" : { + "text" : "Signed overflow check" + }, + "fullDescription" : { + "text" : "Testing for overflow by adding a value to a variable to see if it \"wraps around\" works only for unsigned integer values." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Signed overflow check\nWhen checking for integer overflow, you may often write tests like `a + b < a`. This works fine if `a` or `b` are unsigned integers, since any overflow in the addition will cause the value to simply \"wrap around.\" However, using *signed* integers is problematic because signed overflow has undefined behavior according to the C and C++ standards. If the addition overflows and has an undefined result, the comparison will likewise be undefined; it may produce an unintended result, or may be deleted entirely by an optimizing compiler.\n\n\n## Recommendation\nSolutions to this problem can be thought of as falling into one of two categories:\n\n1. Rewrite the signed expression so that overflow cannot occur but the signedness remains.\n1. Change the variables and all their uses to be unsigned.\nThe following cases all fall into the first category.\n\n1. Given `unsigned short n1, delta` and `n1 + delta < n1`, it is possible to rewrite it as `(unsigned short)(n1 + delta) < n1`. Note that `n1 + delta` does not actually overflow, due to `int` promotion.\n1. Given `unsigned short n1, delta` and `n1 + delta < n1`, it is also possible to rewrite it as `n1 > USHORT_MAX - delta`. The `limits.h` or `climits` header must then be included.\n1. Given `int n1, delta` and `n1 + delta < n1`, it is possible to rewrite it as `n1 > INT_MAX - delta`. It must be true that `delta >= 0` and the `limits.h` or `climits` header has been included.\n\n## Example\nIn the following example, even though `delta` has been declared `unsigned short`, C/C++ type promotion rules require that its type is promoted to the larger type used in the addition and comparison, namely a `signed int`. Addition is performed on signed integers, and may have undefined behavior if an overflow occurs. As a result, the entire (comparison) expression may also have an undefined result.\n\n\n```cpp\nbool foo(int n1, unsigned short delta) {\n return n1 + delta < n1; // BAD\n}\n\n```\nThe following example builds upon the previous one. Instead of performing an addition (which could overflow), we have re-framed the solution so that a subtraction is used instead. Since `delta` is promoted to a `signed int` and `INT_MAX` denotes the largest possible positive value for an `signed int`, the expression `INT_MAX - delta` can never be less than zero or more than `INT_MAX`. Hence, any overflow and underflow are avoided.\n\n\n```cpp\n#include \nbool foo(int n1, unsigned short delta) {\n return n1 > INT_MAX - delta; // GOOD\n}\n\n```\nIn the following example, even though both `n` and `delta` have been declared `unsigned short`, both are promoted to `signed int` prior to addition. Because we started out with the narrower `short` type, the addition is guaranteed not to overflow and is therefore defined. But the fact that `n1 + delta` never overflows means that the condition `n1 + delta < n1` will never hold true, which likely is not what the programmer intended. (see also the `cpp/bad-addition-overflow-check` query).\n\n\n```cpp\nbool bar(unsigned short n1, unsigned short delta) {\n // NB: Comparison is always false\n return n1 + delta < n1; // GOOD (but misleading)\n}\n\n```\nThe next example provides a solution to the previous one. Even though `n1 + delta` does not overflow, casting it to an `unsigned short` truncates the addition modulo 2^16, so that `unsigned short` \"wrap around\" may now be observed. Furthermore, since the left-hand side is now of type `unsigned short`, the right-hand side does not need to be promoted to a `signed int`.\n\n\n```cpp\nbool bar(unsigned short n1, unsigned short delta) {\n return (unsigned short)(n1 + delta) < n1; // GOOD\n}\n\n```\n\n## References\n* [comp.lang.c FAQ list · Question 3.19 (Preserving rules)](http://c-faq.com/expr/preservingrules.html)\n* [INT31-C. Ensure that integer conversions do not result in lost or misinterpreted data](https://wiki.sei.cmu.edu/confluence/display/c/INT31-C.+Ensure+that+integer+conversions+do+not+result+in+lost+or+misinterpreted+data)\n* W. Dietz, P. Li, J. Regehr, V. Adve. [Understanding Integer Overflow in C/C++](https://www.cs.utah.edu/~regehr/papers/overflow12.pdf)\n* Common Weakness Enumeration: [CWE-128](https://cwe.mitre.org/data/definitions/128.html).\n* Common Weakness Enumeration: [CWE-190](https://cwe.mitre.org/data/definitions/190.html).\n", + "markdown" : "# Signed overflow check\nWhen checking for integer overflow, you may often write tests like `a + b < a`. This works fine if `a` or `b` are unsigned integers, since any overflow in the addition will cause the value to simply \"wrap around.\" However, using *signed* integers is problematic because signed overflow has undefined behavior according to the C and C++ standards. If the addition overflows and has an undefined result, the comparison will likewise be undefined; it may produce an unintended result, or may be deleted entirely by an optimizing compiler.\n\n\n## Recommendation\nSolutions to this problem can be thought of as falling into one of two categories:\n\n1. Rewrite the signed expression so that overflow cannot occur but the signedness remains.\n1. Change the variables and all their uses to be unsigned.\nThe following cases all fall into the first category.\n\n1. Given `unsigned short n1, delta` and `n1 + delta < n1`, it is possible to rewrite it as `(unsigned short)(n1 + delta) < n1`. Note that `n1 + delta` does not actually overflow, due to `int` promotion.\n1. Given `unsigned short n1, delta` and `n1 + delta < n1`, it is also possible to rewrite it as `n1 > USHORT_MAX - delta`. The `limits.h` or `climits` header must then be included.\n1. Given `int n1, delta` and `n1 + delta < n1`, it is possible to rewrite it as `n1 > INT_MAX - delta`. It must be true that `delta >= 0` and the `limits.h` or `climits` header has been included.\n\n## Example\nIn the following example, even though `delta` has been declared `unsigned short`, C/C++ type promotion rules require that its type is promoted to the larger type used in the addition and comparison, namely a `signed int`. Addition is performed on signed integers, and may have undefined behavior if an overflow occurs. As a result, the entire (comparison) expression may also have an undefined result.\n\n\n```cpp\nbool foo(int n1, unsigned short delta) {\n return n1 + delta < n1; // BAD\n}\n\n```\nThe following example builds upon the previous one. Instead of performing an addition (which could overflow), we have re-framed the solution so that a subtraction is used instead. Since `delta` is promoted to a `signed int` and `INT_MAX` denotes the largest possible positive value for an `signed int`, the expression `INT_MAX - delta` can never be less than zero or more than `INT_MAX`. Hence, any overflow and underflow are avoided.\n\n\n```cpp\n#include \nbool foo(int n1, unsigned short delta) {\n return n1 > INT_MAX - delta; // GOOD\n}\n\n```\nIn the following example, even though both `n` and `delta` have been declared `unsigned short`, both are promoted to `signed int` prior to addition. Because we started out with the narrower `short` type, the addition is guaranteed not to overflow and is therefore defined. But the fact that `n1 + delta` never overflows means that the condition `n1 + delta < n1` will never hold true, which likely is not what the programmer intended. (see also the `cpp/bad-addition-overflow-check` query).\n\n\n```cpp\nbool bar(unsigned short n1, unsigned short delta) {\n // NB: Comparison is always false\n return n1 + delta < n1; // GOOD (but misleading)\n}\n\n```\nThe next example provides a solution to the previous one. Even though `n1 + delta` does not overflow, casting it to an `unsigned short` truncates the addition modulo 2^16, so that `unsigned short` \"wrap around\" may now be observed. Furthermore, since the left-hand side is now of type `unsigned short`, the right-hand side does not need to be promoted to a `signed int`.\n\n\n```cpp\nbool bar(unsigned short n1, unsigned short delta) {\n return (unsigned short)(n1 + delta) < n1; // GOOD\n}\n\n```\n\n## References\n* [comp.lang.c FAQ list · Question 3.19 (Preserving rules)](http://c-faq.com/expr/preservingrules.html)\n* [INT31-C. Ensure that integer conversions do not result in lost or misinterpreted data](https://wiki.sei.cmu.edu/confluence/display/c/INT31-C.+Ensure+that+integer+conversions+do+not+result+in+lost+or+misinterpreted+data)\n* W. Dietz, P. Li, J. Regehr, V. Adve. [Understanding Integer Overflow in C/C++](https://www.cs.utah.edu/~regehr/papers/overflow12.pdf)\n* Common Weakness Enumeration: [CWE-128](https://cwe.mitre.org/data/definitions/128.html).\n* Common Weakness Enumeration: [CWE-190](https://cwe.mitre.org/data/definitions/190.html).\n" + }, + "properties" : { + "tags" : [ "correctness", "security", "external/cwe/cwe-128", "external/cwe/cwe-190" ], + "description" : "Testing for overflow by adding a value to a variable\n to see if it \"wraps around\" works only for\n unsigned integer values.", + "id" : "cpp/signed-overflow-check", + "kind" : "problem", + "name" : "Signed overflow check", + "precision" : "high", + "problem.severity" : "warning", + "security-severity" : "8.1" + } + }, { + "id" : "cpp/bad-addition-overflow-check", + "name" : "cpp/bad-addition-overflow-check", + "shortDescription" : { + "text" : "Bad check for overflow of integer addition" + }, + "fullDescription" : { + "text" : "Checking for overflow of integer addition by comparing against one of the arguments of the addition does not work when the result of the addition is automatically promoted to a larger type." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Bad check for overflow of integer addition\nChecking for overflow of integer addition needs to be done with care, because automatic type promotion can prevent the check from working as intended, with the same value (`true` or `false`) always being returned.\n\n\n## Recommendation\nUse an explicit cast to make sure that the result of the addition is not implicitly converted to a larger type.\n\n\n## Example\n\n```cpp\nbool checkOverflow(unsigned short x, unsigned short y) {\n // BAD: comparison is always false due to type promotion\n return (x + y < x); \n}\n\n```\nOn a typical architecture where `short` is 16 bits and `int` is 32 bits, the operands of the addition are automatically promoted to `int`, so it cannot overflow and the result of the comparison is always false.\n\nThe code below implements the check correctly, by using an explicit cast to make sure that the result of the addition is `unsigned short` (which may overflow, in which case the comparison would evaluate to `true`).\n\n\n```cpp\nbool checkOverflow(unsigned short x, unsigned short y) {\n return ((unsigned short)(x + y) < x); // GOOD: explicit cast\n}\n\n```\n\n## References\n* [Preserving Rules](http://c-faq.com/expr/preservingrules.html)\n* [Understand integer conversion rules](https://www.securecoding.cert.org/confluence/plugins/servlet/mobile#content/view/20086942)\n* Common Weakness Enumeration: [CWE-190](https://cwe.mitre.org/data/definitions/190.html).\n* Common Weakness Enumeration: [CWE-192](https://cwe.mitre.org/data/definitions/192.html).\n", + "markdown" : "# Bad check for overflow of integer addition\nChecking for overflow of integer addition needs to be done with care, because automatic type promotion can prevent the check from working as intended, with the same value (`true` or `false`) always being returned.\n\n\n## Recommendation\nUse an explicit cast to make sure that the result of the addition is not implicitly converted to a larger type.\n\n\n## Example\n\n```cpp\nbool checkOverflow(unsigned short x, unsigned short y) {\n // BAD: comparison is always false due to type promotion\n return (x + y < x); \n}\n\n```\nOn a typical architecture where `short` is 16 bits and `int` is 32 bits, the operands of the addition are automatically promoted to `int`, so it cannot overflow and the result of the comparison is always false.\n\nThe code below implements the check correctly, by using an explicit cast to make sure that the result of the addition is `unsigned short` (which may overflow, in which case the comparison would evaluate to `true`).\n\n\n```cpp\nbool checkOverflow(unsigned short x, unsigned short y) {\n return ((unsigned short)(x + y) < x); // GOOD: explicit cast\n}\n\n```\n\n## References\n* [Preserving Rules](http://c-faq.com/expr/preservingrules.html)\n* [Understand integer conversion rules](https://www.securecoding.cert.org/confluence/plugins/servlet/mobile#content/view/20086942)\n* Common Weakness Enumeration: [CWE-190](https://cwe.mitre.org/data/definitions/190.html).\n* Common Weakness Enumeration: [CWE-192](https://cwe.mitre.org/data/definitions/192.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "correctness", "security", "external/cwe/cwe-190", "external/cwe/cwe-192" ], + "description" : "Checking for overflow of integer addition by comparing\n against one of the arguments of the addition does not work\n when the result of the addition is automatically promoted\n to a larger type.", + "id" : "cpp/bad-addition-overflow-check", + "kind" : "problem", + "name" : "Bad check for overflow of integer addition", + "precision" : "very-high", + "problem.severity" : "error", + "security-severity" : "8.1" + } + }, { + "id" : "cpp/unsafe-use-of-this", + "name" : "cpp/unsafe-use-of-this", + "shortDescription" : { + "text" : "Unsafe use of this in constructor" + }, + "fullDescription" : { + "text" : "A call to a pure virtual function using a 'this' pointer of an object that is under construction may lead to undefined behavior." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Unsafe use of this in constructor\nThis rule finds calls to pure virtual member functions in constructors and destructors. When executing the body of a constructor of class `T`, the virtual table of `T` refers to the virtual table of one of `T`'s base classes. This can produce unexpected behavior, including program abort that can lead to denial of service attacks. The same problem exists during destruction of an object.\n\n\n## Recommendation\nDo not rely on virtual dispatch in constructors and destructors. Instead, each class should be responsible for acquiring and releasing its resources. If a base class needs to refer to a derived class during initialization, use the Dynamic Binding During Initialization idiom.\n\n\n## Example\n\n```cpp\nclass Base {\nprivate:\n // pure virtual member function used for initialization of derived classes.\n virtual void construct() = 0;\npublic:\n Base() {\n // wrong: the virtual table of `Derived` has not been initialized yet. So this\n // call will resolve to `Base::construct`, which cannot be called as it is a pure\n // virtual function.\n construct();\n }\n};\n\nclass Derived : public Base {\n int field;\n\n void construct() override {\n field = 1;\n }\n};\n\n```\n\n## References\n* ISO C++ FAQ: [When my base class's constructor calls a virtual function on its this object, why doesn't my derived class's override of that virtual function get invoked?](https://isocpp.org/wiki/faq/strange-inheritance#calling-virtuals-from-ctors)\n* SEI CERT C++ Coding Standard [OOP50-CPP. Do not invoke virtual functions from constructors or destructors](https://wiki.sei.cmu.edu/confluence/display/cplusplus/OOP50-CPP.+Do+not+invoke+virtual+functions+from+constructors+or+destructors)\n* ISO C++ FAQ: [Okay, but is there a way to simulate that behavior as if dynamic binding worked on the this object within my base class's constructor?](https://isocpp.org/wiki/faq/strange-inheritance#calling-virtuals-from-ctor-idiom)\n* Common Weakness Enumeration: [CWE-670](https://cwe.mitre.org/data/definitions/670.html).\n", + "markdown" : "# Unsafe use of this in constructor\nThis rule finds calls to pure virtual member functions in constructors and destructors. When executing the body of a constructor of class `T`, the virtual table of `T` refers to the virtual table of one of `T`'s base classes. This can produce unexpected behavior, including program abort that can lead to denial of service attacks. The same problem exists during destruction of an object.\n\n\n## Recommendation\nDo not rely on virtual dispatch in constructors and destructors. Instead, each class should be responsible for acquiring and releasing its resources. If a base class needs to refer to a derived class during initialization, use the Dynamic Binding During Initialization idiom.\n\n\n## Example\n\n```cpp\nclass Base {\nprivate:\n // pure virtual member function used for initialization of derived classes.\n virtual void construct() = 0;\npublic:\n Base() {\n // wrong: the virtual table of `Derived` has not been initialized yet. So this\n // call will resolve to `Base::construct`, which cannot be called as it is a pure\n // virtual function.\n construct();\n }\n};\n\nclass Derived : public Base {\n int field;\n\n void construct() override {\n field = 1;\n }\n};\n\n```\n\n## References\n* ISO C++ FAQ: [When my base class's constructor calls a virtual function on its this object, why doesn't my derived class's override of that virtual function get invoked?](https://isocpp.org/wiki/faq/strange-inheritance#calling-virtuals-from-ctors)\n* SEI CERT C++ Coding Standard [OOP50-CPP. Do not invoke virtual functions from constructors or destructors](https://wiki.sei.cmu.edu/confluence/display/cplusplus/OOP50-CPP.+Do+not+invoke+virtual+functions+from+constructors+or+destructors)\n* ISO C++ FAQ: [Okay, but is there a way to simulate that behavior as if dynamic binding worked on the this object within my base class's constructor?](https://isocpp.org/wiki/faq/strange-inheritance#calling-virtuals-from-ctor-idiom)\n* Common Weakness Enumeration: [CWE-670](https://cwe.mitre.org/data/definitions/670.html).\n" + }, + "properties" : { + "tags" : [ "correctness", "language-features", "security", "external/cwe/cwe-670" ], + "description" : "A call to a pure virtual function using a 'this'\n pointer of an object that is under construction\n may lead to undefined behavior.", + "id" : "cpp/unsafe-use-of-this", + "kind" : "path-problem", + "name" : "Unsafe use of this in constructor", + "precision" : "very-high", + "problem.severity" : "error", + "security-severity" : "7.5" + } + }, { + "id" : "cpp/alloca-in-loop", + "name" : "cpp/alloca-in-loop", + "shortDescription" : { + "text" : "Call to alloca in a loop" + }, + "fullDescription" : { + "text" : "Using alloca in a loop can lead to a stack overflow" + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Call to alloca in a loop\nThe `alloca` macro allocates memory by expanding the current stack frame. Invoking `alloca` within a loop may lead to a stack overflow because the memory is not released until the function returns.\n\n\n## Recommendation\nConsider invoking `alloca` once outside the loop, or using `malloc` or `new` to allocate memory on the heap if the allocation must be done inside the loop.\n\n\n## Example\nThe variable `path` is allocated inside a loop with `alloca`. Consequently, storage for all copies of the path is present in the stack frame until the end of the function.\n\n\n```cpp\nchar *dir_path;\nchar **dir_entries;\nint count;\n\nfor (int i = 0; i < count; i++) {\n char *path = (char*)alloca(strlen(dir_path) + strlen(dir_entry[i]) + 2);\n // use path\n}\n\n```\nIn the revised example, `path` is allocated with `malloc` and freed at the end of the loop.\n\n\n```cpp\nchar *dir_path;\nchar **dir_entries;\nint count;\n\nfor (int i = 0; i < count; i++) {\n char *path = (char*)malloc(strlen(dir_path) + strlen(dir_entry[i]) + 2);\n // use path\n free(path);\n}\n\n```\n\n## References\n* Linux Programmer's Manual: [ALLOCA(3)](http://man7.org/linux/man-pages/man3/alloca.3.html).\n* Common Weakness Enumeration: [CWE-770](https://cwe.mitre.org/data/definitions/770.html).\n", + "markdown" : "# Call to alloca in a loop\nThe `alloca` macro allocates memory by expanding the current stack frame. Invoking `alloca` within a loop may lead to a stack overflow because the memory is not released until the function returns.\n\n\n## Recommendation\nConsider invoking `alloca` once outside the loop, or using `malloc` or `new` to allocate memory on the heap if the allocation must be done inside the loop.\n\n\n## Example\nThe variable `path` is allocated inside a loop with `alloca`. Consequently, storage for all copies of the path is present in the stack frame until the end of the function.\n\n\n```cpp\nchar *dir_path;\nchar **dir_entries;\nint count;\n\nfor (int i = 0; i < count; i++) {\n char *path = (char*)alloca(strlen(dir_path) + strlen(dir_entry[i]) + 2);\n // use path\n}\n\n```\nIn the revised example, `path` is allocated with `malloc` and freed at the end of the loop.\n\n\n```cpp\nchar *dir_path;\nchar **dir_entries;\nint count;\n\nfor (int i = 0; i < count; i++) {\n char *path = (char*)malloc(strlen(dir_path) + strlen(dir_entry[i]) + 2);\n // use path\n free(path);\n}\n\n```\n\n## References\n* Linux Programmer's Manual: [ALLOCA(3)](http://man7.org/linux/man-pages/man3/alloca.3.html).\n* Common Weakness Enumeration: [CWE-770](https://cwe.mitre.org/data/definitions/770.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "correctness", "security", "external/cwe/cwe-770" ], + "description" : "Using alloca in a loop can lead to a stack overflow", + "id" : "cpp/alloca-in-loop", + "kind" : "problem", + "name" : "Call to alloca in a loop", + "precision" : "high", + "problem.severity" : "warning", + "security-severity" : "7.5" + } + }, { + "id" : "cpp/using-expired-stack-address", + "name" : "cpp/using-expired-stack-address", + "shortDescription" : { + "text" : "Use of expired stack-address" + }, + "fullDescription" : { + "text" : "Accessing the stack-allocated memory of a function after it has returned can lead to memory corruption." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Use of expired stack-address\nThis rule finds uses of pointers that likely point to local variables in expired stack frames. A pointer to a local variable is only valid until the function returns, after which it becomes a dangling pointer.\n\n\n## Recommendation\n1. If it is necessary to take the address of a local variable, then make sure that the address is only stored in memory that does not outlive the local variable. For example, it is safe to store the address in another local variable. Similarly, it is also safe to pass the address of a local variable to another function provided that the other function only uses it locally and does not store it in non-local memory.\n1. If it is necessary to store an address which will outlive the current function scope, then it should be allocated on the heap. Care should be taken to make sure that the memory is deallocated when it is no longer needed, particularly when using low-level memory management routines such as `malloc`/`free` or `new`/`delete`. Modern C++ applications often use smart pointers, such as `std::shared_ptr`, to reduce the chance of a memory leak.\n\n## Example\n\n```cpp\nstatic const int* xptr;\n\nvoid localAddressEscapes() {\n int x = 0;\n xptr = &x;\n}\n\nvoid example1() {\n localAddressEscapes();\n const int* x = xptr; // BAD: This pointer points to expired stack allocated memory.\n}\n\nvoid localAddressDoesNotEscape() {\n int x = 0;\n xptr = &x;\n // ...\n // use `xptr`\n // ...\n xptr = nullptr;\n}\n\nvoid example2() {\n localAddressDoesNotEscape();\n const int* x = xptr; // GOOD: This pointer does not point to expired memory.\n}\n\n```\n\n## References\n* Wikipedia: [Dangling pointer](https://en.wikipedia.org/wiki/Dangling_pointer).\n* Common Weakness Enumeration: [CWE-825](https://cwe.mitre.org/data/definitions/825.html).\n", + "markdown" : "# Use of expired stack-address\nThis rule finds uses of pointers that likely point to local variables in expired stack frames. A pointer to a local variable is only valid until the function returns, after which it becomes a dangling pointer.\n\n\n## Recommendation\n1. If it is necessary to take the address of a local variable, then make sure that the address is only stored in memory that does not outlive the local variable. For example, it is safe to store the address in another local variable. Similarly, it is also safe to pass the address of a local variable to another function provided that the other function only uses it locally and does not store it in non-local memory.\n1. If it is necessary to store an address which will outlive the current function scope, then it should be allocated on the heap. Care should be taken to make sure that the memory is deallocated when it is no longer needed, particularly when using low-level memory management routines such as `malloc`/`free` or `new`/`delete`. Modern C++ applications often use smart pointers, such as `std::shared_ptr`, to reduce the chance of a memory leak.\n\n## Example\n\n```cpp\nstatic const int* xptr;\n\nvoid localAddressEscapes() {\n int x = 0;\n xptr = &x;\n}\n\nvoid example1() {\n localAddressEscapes();\n const int* x = xptr; // BAD: This pointer points to expired stack allocated memory.\n}\n\nvoid localAddressDoesNotEscape() {\n int x = 0;\n xptr = &x;\n // ...\n // use `xptr`\n // ...\n xptr = nullptr;\n}\n\nvoid example2() {\n localAddressDoesNotEscape();\n const int* x = xptr; // GOOD: This pointer does not point to expired memory.\n}\n\n```\n\n## References\n* Wikipedia: [Dangling pointer](https://en.wikipedia.org/wiki/Dangling_pointer).\n* Common Weakness Enumeration: [CWE-825](https://cwe.mitre.org/data/definitions/825.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "security", "external/cwe/cwe-825" ], + "description" : "Accessing the stack-allocated memory of a function\n after it has returned can lead to memory corruption.", + "id" : "cpp/using-expired-stack-address", + "kind" : "path-problem", + "name" : "Use of expired stack-address", + "precision" : "high", + "problem.severity" : "error", + "security-severity" : "9.3" + } + }, { + "id" : "cpp/return-stack-allocated-memory", + "name" : "cpp/return-stack-allocated-memory", + "shortDescription" : { + "text" : "Returning stack-allocated memory" + }, + "fullDescription" : { + "text" : "A function returns a pointer to a stack-allocated region of memory. This memory is deallocated at the end of the function, which may lead the caller to dereference a dangling pointer." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Returning stack-allocated memory\nThis rule finds return statements that return pointers to an object allocated on the stack. The lifetime of a stack allocated memory location only lasts until the function returns, and the contents of that memory become undefined after that. Clearly, using a pointer to stack memory after the function has already returned will have undefined results.\n\n\n## Recommendation\nUse the functions of the `malloc` family to dynamically allocate memory on the heap for data that is used across function calls.\n\n\n## Example\n\n```cpp\nRecord* fixRecord(Record* r) {\n\tRecord myRecord = *r;\n\tdelete r;\n\n\tmyRecord.fix();\n\treturn &myRecord; //returns reference to myRecord, which is a stack-allocated object\n}\n```\n\n## References\n* Common Weakness Enumeration: [CWE-825](https://cwe.mitre.org/data/definitions/825.html).\n", + "markdown" : "# Returning stack-allocated memory\nThis rule finds return statements that return pointers to an object allocated on the stack. The lifetime of a stack allocated memory location only lasts until the function returns, and the contents of that memory become undefined after that. Clearly, using a pointer to stack memory after the function has already returned will have undefined results.\n\n\n## Recommendation\nUse the functions of the `malloc` family to dynamically allocate memory on the heap for data that is used across function calls.\n\n\n## Example\n\n```cpp\nRecord* fixRecord(Record* r) {\n\tRecord myRecord = *r;\n\tdelete r;\n\n\tmyRecord.fix();\n\treturn &myRecord; //returns reference to myRecord, which is a stack-allocated object\n}\n```\n\n## References\n* Common Weakness Enumeration: [CWE-825](https://cwe.mitre.org/data/definitions/825.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "security", "external/cwe/cwe-825" ], + "description" : "A function returns a pointer to a stack-allocated region of\n memory. This memory is deallocated at the end of the function,\n which may lead the caller to dereference a dangling pointer.", + "id" : "cpp/return-stack-allocated-memory", + "kind" : "path-problem", + "name" : "Returning stack-allocated memory", + "precision" : "high", + "problem.severity" : "warning", + "security-severity" : "9.3" + } + }, { + "id" : "cpp/pointer-overflow-check", + "name" : "cpp/pointer-overflow-check", + "shortDescription" : { + "text" : "Pointer overflow check" + }, + "fullDescription" : { + "text" : "Adding a value to a pointer to check if it overflows relies on undefined behavior and may lead to memory corruption." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Pointer overflow check\nWhen checking for integer overflow, you may often write tests like `p + i < p`. This works fine if `p` and `i` are unsigned integers, since any overflow in the addition will cause the value to simply \"wrap around.\" However, using this pattern when `p` is a pointer is problematic because pointer overflow has undefined behavior according to the C and C++ standards. If the addition overflows and has an undefined result, the comparison will likewise be undefined; it may produce an unintended result, or may be deleted entirely by an optimizing compiler.\n\n\n## Recommendation\nTo check whether an index `i` is less than the length of an array, simply compare these two numbers as unsigned integers: `i < ARRAY_LENGTH`. If the length of the array is defined as the difference between two pointers `ptr` and `p_end`, write `i < p_end - ptr`. If `i` is signed, cast it to unsigned in order to guard against negative `i`. For example, write `(size_t)i < p_end - ptr`.\n\n\n## Example\nAn invalid check for pointer overflow is most often seen as part of checking whether a number `a` is too large by checking first if adding the number to `ptr` goes past the end of an allocation and then checking if adding it to `ptr` creates a pointer so large that it overflows and wraps around.\n\n\n```cpp\nbool not_in_range(T *ptr, T *ptr_end, size_t i) {\n return ptr + i >= ptr_end || ptr + i < ptr; // BAD\n}\n\n```\nIn both of these checks, the operations are performed in the wrong order. First, an expression that may cause undefined behavior is evaluated (`ptr + i`), and then the result is checked for being in range. But once undefined behavior has happened in the pointer addition, it cannot be recovered from: it's too late to perform the range check after a possible pointer overflow.\n\nWhile it's not the subject of this query, the expression `ptr + i < ptr_end` is also an invalid range check. It's undefined behavior in C/C++ to create a pointer that points more than one past the end of an allocation.\n\nThe next example shows how to portably check whether an unsigned number is outside the range of an allocation between `ptr` and `ptr_end`.\n\n\n```cpp\nbool not_in_range(T *ptr, T *ptr_end, size_t i) {\n return i >= ptr_end - ptr; // GOOD\n}\n```\n\n## References\n* Embedded in Academia: [Pointer Overflow Checking](https://blog.regehr.org/archives/1395).\n* LWN: [GCC and pointer overflows](https://lwn.net/Articles/278137/).\n* Common Weakness Enumeration: [CWE-758](https://cwe.mitre.org/data/definitions/758.html).\n", + "markdown" : "# Pointer overflow check\nWhen checking for integer overflow, you may often write tests like `p + i < p`. This works fine if `p` and `i` are unsigned integers, since any overflow in the addition will cause the value to simply \"wrap around.\" However, using this pattern when `p` is a pointer is problematic because pointer overflow has undefined behavior according to the C and C++ standards. If the addition overflows and has an undefined result, the comparison will likewise be undefined; it may produce an unintended result, or may be deleted entirely by an optimizing compiler.\n\n\n## Recommendation\nTo check whether an index `i` is less than the length of an array, simply compare these two numbers as unsigned integers: `i < ARRAY_LENGTH`. If the length of the array is defined as the difference between two pointers `ptr` and `p_end`, write `i < p_end - ptr`. If `i` is signed, cast it to unsigned in order to guard against negative `i`. For example, write `(size_t)i < p_end - ptr`.\n\n\n## Example\nAn invalid check for pointer overflow is most often seen as part of checking whether a number `a` is too large by checking first if adding the number to `ptr` goes past the end of an allocation and then checking if adding it to `ptr` creates a pointer so large that it overflows and wraps around.\n\n\n```cpp\nbool not_in_range(T *ptr, T *ptr_end, size_t i) {\n return ptr + i >= ptr_end || ptr + i < ptr; // BAD\n}\n\n```\nIn both of these checks, the operations are performed in the wrong order. First, an expression that may cause undefined behavior is evaluated (`ptr + i`), and then the result is checked for being in range. But once undefined behavior has happened in the pointer addition, it cannot be recovered from: it's too late to perform the range check after a possible pointer overflow.\n\nWhile it's not the subject of this query, the expression `ptr + i < ptr_end` is also an invalid range check. It's undefined behavior in C/C++ to create a pointer that points more than one past the end of an allocation.\n\nThe next example shows how to portably check whether an unsigned number is outside the range of an allocation between `ptr` and `ptr_end`.\n\n\n```cpp\nbool not_in_range(T *ptr, T *ptr_end, size_t i) {\n return i >= ptr_end - ptr; // GOOD\n}\n```\n\n## References\n* Embedded in Academia: [Pointer Overflow Checking](https://blog.regehr.org/archives/1395).\n* LWN: [GCC and pointer overflows](https://lwn.net/Articles/278137/).\n* Common Weakness Enumeration: [CWE-758](https://cwe.mitre.org/data/definitions/758.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "security", "external/cwe/cwe-758" ], + "description" : "Adding a value to a pointer to check if it overflows relies\n on undefined behavior and may lead to memory corruption.", + "id" : "cpp/pointer-overflow-check", + "kind" : "problem", + "name" : "Pointer overflow check", + "precision" : "high", + "problem.severity" : "error", + "security-severity" : "2.1" + } + }, { + "id" : "cpp/redundant-null-check-simple", + "name" : "cpp/redundant-null-check-simple", + "shortDescription" : { + "text" : "Redundant null check due to previous dereference" + }, + "fullDescription" : { + "text" : "Checking a pointer for nullness after dereferencing it is likely to be a sign that either the check can be removed, or it should be moved before the dereference." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Redundant null check due to previous dereference\nThis rule finds comparisons of a pointer to null that occur after a reference of that pointer. It's likely either the check is not required and can be removed, or it should be moved to before the dereference so that a null pointer dereference does not occur.\n\n\n## Recommendation\nThe check should be moved to before the dereference, in a way that prevents a null pointer value from being dereferenced. If it's clear that the pointer cannot be null, consider removing the check instead.\n\n\n## Example\n\n```cpp\nint f(MyList *list) {\n\tlist->append(1);\n\n\t// ...\n\n\tif (list != NULL)\n\t{\n\t\tlist->append(2);\n\t}\n}\n\n```\n\n## References\n* [ Null Dereference ](https://www.owasp.org/index.php/Null_Dereference)\n* Common Weakness Enumeration: [CWE-476](https://cwe.mitre.org/data/definitions/476.html).\n", + "markdown" : "# Redundant null check due to previous dereference\nThis rule finds comparisons of a pointer to null that occur after a reference of that pointer. It's likely either the check is not required and can be removed, or it should be moved to before the dereference so that a null pointer dereference does not occur.\n\n\n## Recommendation\nThe check should be moved to before the dereference, in a way that prevents a null pointer value from being dereferenced. If it's clear that the pointer cannot be null, consider removing the check instead.\n\n\n## Example\n\n```cpp\nint f(MyList *list) {\n\tlist->append(1);\n\n\t// ...\n\n\tif (list != NULL)\n\t{\n\t\tlist->append(2);\n\t}\n}\n\n```\n\n## References\n* [ Null Dereference ](https://www.owasp.org/index.php/Null_Dereference)\n* Common Weakness Enumeration: [CWE-476](https://cwe.mitre.org/data/definitions/476.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "correctness", "security", "external/cwe/cwe-476" ], + "description" : "Checking a pointer for nullness after dereferencing it is\n likely to be a sign that either the check can be removed, or\n it should be moved before the dereference.", + "id" : "cpp/redundant-null-check-simple", + "kind" : "path-problem", + "name" : "Redundant null check due to previous dereference", + "precision" : "high", + "problem.severity" : "error", + "security-severity" : "7.5" + } + }, { + "id" : "cpp/upcast-array-pointer-arithmetic", + "name" : "cpp/upcast-array-pointer-arithmetic", + "shortDescription" : { + "text" : "Upcast array used in pointer arithmetic" + }, + "fullDescription" : { + "text" : "An array with elements of a derived struct type is cast to a pointer to the base type of the struct. If pointer arithmetic or an array dereference is done on the resulting pointer, it will use the width of the base type, leading to misaligned reads." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Upcast array used in pointer arithmetic\nA pointer to a derived class may be implicitly converted to a pointer to its base type when passed as an argument to a function expecting a pointer to the base type. If pointer arithmetic or an array dereference is then used, it will be performed using the size of the base type. This can lead to reading data from unexpected fields in the derived type.\n\n\n## Recommendation\nOnly convert pointers to single objects. If you must work with a sequence of objects that are converted to a base type, use an array of pointers rather than a pointer to an array.\n\n\n## Example\n\n```cpp\nclass Base {\npublic:\n\tint x;\n}\n\nclass Derived: public Base {\npublic:\n\tint y;\n};\n\nvoid dereference_base(Base *b) {\n\tb[2].x;\n}\n\nvoid dereference_derived(Derived *d) {\n\td[2].x;\n}\n\nvoid test () {\n\tDerived[4] d;\n\tdereference_base(d); // BAD: implicit conversion to Base*\n\n\tdereference_derived(d); // GOOD: implicit conversion to Derived*, which will be the right size\n}\n\n```\n", + "markdown" : "# Upcast array used in pointer arithmetic\nA pointer to a derived class may be implicitly converted to a pointer to its base type when passed as an argument to a function expecting a pointer to the base type. If pointer arithmetic or an array dereference is then used, it will be performed using the size of the base type. This can lead to reading data from unexpected fields in the derived type.\n\n\n## Recommendation\nOnly convert pointers to single objects. If you must work with a sequence of objects that are converted to a base type, use an array of pointers rather than a pointer to an array.\n\n\n## Example\n\n```cpp\nclass Base {\npublic:\n\tint x;\n}\n\nclass Derived: public Base {\npublic:\n\tint y;\n};\n\nvoid dereference_base(Base *b) {\n\tb[2].x;\n}\n\nvoid dereference_derived(Derived *d) {\n\td[2].x;\n}\n\nvoid test () {\n\tDerived[4] d;\n\tdereference_base(d); // BAD: implicit conversion to Base*\n\n\tdereference_derived(d); // GOOD: implicit conversion to Derived*, which will be the right size\n}\n\n```\n" + }, + "properties" : { + "tags" : [ "correctness", "reliability", "security", "external/cwe/cwe-119", "external/cwe/cwe-843" ], + "description" : "An array with elements of a derived struct type is cast to a\n pointer to the base type of the struct. If pointer arithmetic or\n an array dereference is done on the resulting pointer, it will\n use the width of the base type, leading to misaligned reads.", + "id" : "cpp/upcast-array-pointer-arithmetic", + "kind" : "path-problem", + "name" : "Upcast array used in pointer arithmetic", + "precision" : "high", + "problem.severity" : "warning", + "security-severity" : "9.3" + } + }, { + "id" : "cpp/too-few-arguments", + "name" : "cpp/too-few-arguments", + "shortDescription" : { + "text" : "Call to function with fewer arguments than declared parameters" + }, + "fullDescription" : { + "text" : "A function call is passing fewer arguments than the number of declared parameters of the function. This may indicate that the code does not follow the author's intent. It is also a vulnerability, since the function is likely to operate on undefined data." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Call to function with fewer arguments than declared parameters\nA function is called with fewer arguments than there are parameters of the function.\n\nThis may indicate that an incorrect function is being called, or that the signature (parameter list) of the called function is not known to the author.\n\nIn C, function calls generally need to provide the same number of arguments as there are arguments to the function. (Variadic functions can accept additional arguments.) Providing fewer arguments than there are parameters is extremely dangerous, as the called function will nevertheless try to obtain the missing arguments' values, either from the stack or from machine registers. As a result, the function may behave unpredictably.\n\nIf the called function *modifies* a parameter corresponding to a missing argument, it may alter the state of the program upon its return. An attacker could use this to, for example, alter the control flow of the program to access forbidden resources.\n\n\n## Recommendation\nCall the function with the correct number of arguments.\n\n\n## Example\n\n```c\nvoid one_argument();\n\nvoid calls() {\n\tone_argument(1); // GOOD: `one_argument` will accept and use the argument\n\t\n\tone_argument(); // BAD: `one_argument` will receive an undefined value\n}\n\nvoid one_argument(int x);\n\n```\n\n## References\n* SEI CERT C Coding Standard: [ DCL20-C. Explicitly specify void when a function accepts no arguments ](https://wiki.sei.cmu.edu/confluence/display/c/DCL20-C.+Explicitly+specify+void+when+a+function+accepts+no+arguments)\n* Common Weakness Enumeration: [CWE-234](https://cwe.mitre.org/data/definitions/234.html).\n* Common Weakness Enumeration: [CWE-685](https://cwe.mitre.org/data/definitions/685.html).\n", + "markdown" : "# Call to function with fewer arguments than declared parameters\nA function is called with fewer arguments than there are parameters of the function.\n\nThis may indicate that an incorrect function is being called, or that the signature (parameter list) of the called function is not known to the author.\n\nIn C, function calls generally need to provide the same number of arguments as there are arguments to the function. (Variadic functions can accept additional arguments.) Providing fewer arguments than there are parameters is extremely dangerous, as the called function will nevertheless try to obtain the missing arguments' values, either from the stack or from machine registers. As a result, the function may behave unpredictably.\n\nIf the called function *modifies* a parameter corresponding to a missing argument, it may alter the state of the program upon its return. An attacker could use this to, for example, alter the control flow of the program to access forbidden resources.\n\n\n## Recommendation\nCall the function with the correct number of arguments.\n\n\n## Example\n\n```c\nvoid one_argument();\n\nvoid calls() {\n\tone_argument(1); // GOOD: `one_argument` will accept and use the argument\n\t\n\tone_argument(); // BAD: `one_argument` will receive an undefined value\n}\n\nvoid one_argument(int x);\n\n```\n\n## References\n* SEI CERT C Coding Standard: [ DCL20-C. Explicitly specify void when a function accepts no arguments ](https://wiki.sei.cmu.edu/confluence/display/c/DCL20-C.+Explicitly+specify+void+when+a+function+accepts+no+arguments)\n* Common Weakness Enumeration: [CWE-234](https://cwe.mitre.org/data/definitions/234.html).\n* Common Weakness Enumeration: [CWE-685](https://cwe.mitre.org/data/definitions/685.html).\n" + }, + "properties" : { + "tags" : [ "correctness", "maintainability", "security", "external/cwe/cwe-234", "external/cwe/cwe-685" ], + "description" : "A function call is passing fewer arguments than the number of\n declared parameters of the function. This may indicate\n that the code does not follow the author's intent. It is also\n a vulnerability, since the function is likely to operate on\n undefined data.", + "id" : "cpp/too-few-arguments", + "kind" : "problem", + "name" : "Call to function with fewer arguments than declared parameters", + "precision" : "very-high", + "problem.severity" : "error", + "security-severity" : "5.0" + } + }, { + "id" : "cpp/memset-may-be-deleted", + "name" : "cpp/memset-may-be-deleted", + "shortDescription" : { + "text" : "Call to `memset` may be deleted" + }, + "fullDescription" : { + "text" : "Using the `memset` function to clear private data in a variable that has no subsequent use can make information-leak vulnerabilities easier to exploit because the compiler can remove the call." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Call to `memset` may be deleted\nCalling `memset` or `bzero` on a buffer to clear its contents may get optimized away by the compiler if the buffer is not subsequently used. This is not desirable behavior if the buffer contains sensitive data that could somehow be retrieved by an attacker.\n\n\n## Recommendation\nUse alternative platform-supplied functions that will not get optimized away. Examples of such functions include `memset_s`, `SecureZeroMemory`, and `bzero_explicit`. Alternatively, passing the `-fno-builtin-memset` option to the GCC/Clang compiler usually also prevents the optimization. Finally, you can use the public-domain `secure_memzero` function (see references below). This function, however, is not guaranteed to work on all platforms and compilers.\n\n\n## Example\nThe following program fragment uses `memset` to erase sensitive information after it is no longer needed:\n\n\n```c\nchar password[MAX_PASSWORD_LENGTH];\n// read and verify password\nmemset(password, 0, MAX_PASSWORD_LENGTH);\n\n```\nBecause of dead store elimination, the call to `memset` may be removed by the compiler (since the buffer is not subsequently used), resulting in potentially sensitive data remaining in memory.\n\nThe best solution to this problem is to use the `memset_s` function instead of `memset`:\n\n\n```c\nchar password[MAX_PASSWORD_LENGTH];\n// read and verify password\nmemset_s(password, MAX_PASSWORD_LENGTH, 0, MAX_PASSWORD_LENGTH);\n\n```\n\n## References\n* CERT C Coding Standard: [MSC06-C. Beware of compiler optimizations](https://wiki.sei.cmu.edu/confluence/display/c/MSC06-C.+Beware+of+compiler+optimizations).\n* USENIX: The Advanced Computing Systems Association: [Dead Store Elimination (Still) Considered Harmfuls](https://www.usenix.org/system/files/conference/usenixsecurity17/sec17-yang.pdf)\n* Common Weakness Enumeration: [CWE-14](https://cwe.mitre.org/data/definitions/14.html).\n", + "markdown" : "# Call to `memset` may be deleted\nCalling `memset` or `bzero` on a buffer to clear its contents may get optimized away by the compiler if the buffer is not subsequently used. This is not desirable behavior if the buffer contains sensitive data that could somehow be retrieved by an attacker.\n\n\n## Recommendation\nUse alternative platform-supplied functions that will not get optimized away. Examples of such functions include `memset_s`, `SecureZeroMemory`, and `bzero_explicit`. Alternatively, passing the `-fno-builtin-memset` option to the GCC/Clang compiler usually also prevents the optimization. Finally, you can use the public-domain `secure_memzero` function (see references below). This function, however, is not guaranteed to work on all platforms and compilers.\n\n\n## Example\nThe following program fragment uses `memset` to erase sensitive information after it is no longer needed:\n\n\n```c\nchar password[MAX_PASSWORD_LENGTH];\n// read and verify password\nmemset(password, 0, MAX_PASSWORD_LENGTH);\n\n```\nBecause of dead store elimination, the call to `memset` may be removed by the compiler (since the buffer is not subsequently used), resulting in potentially sensitive data remaining in memory.\n\nThe best solution to this problem is to use the `memset_s` function instead of `memset`:\n\n\n```c\nchar password[MAX_PASSWORD_LENGTH];\n// read and verify password\nmemset_s(password, MAX_PASSWORD_LENGTH, 0, MAX_PASSWORD_LENGTH);\n\n```\n\n## References\n* CERT C Coding Standard: [MSC06-C. Beware of compiler optimizations](https://wiki.sei.cmu.edu/confluence/display/c/MSC06-C.+Beware+of+compiler+optimizations).\n* USENIX: The Advanced Computing Systems Association: [Dead Store Elimination (Still) Considered Harmfuls](https://www.usenix.org/system/files/conference/usenixsecurity17/sec17-yang.pdf)\n* Common Weakness Enumeration: [CWE-14](https://cwe.mitre.org/data/definitions/14.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-14" ], + "description" : "Using the `memset` function to clear private data in a variable that has no subsequent use\n can make information-leak vulnerabilities easier to exploit because the compiler can remove the call.", + "id" : "cpp/memset-may-be-deleted", + "kind" : "problem", + "name" : "Call to `memset` may be deleted", + "precision" : "high", + "problem.severity" : "warning", + "security-severity" : "7.8" + } + }, { + "id" : "cpp/incorrect-string-type-conversion", + "name" : "cpp/incorrect-string-type-conversion", + "shortDescription" : { + "text" : "Cast from char* to wchar_t*" + }, + "fullDescription" : { + "text" : "Casting a byte string to a wide-character string is likely to yield a string that is incorrectly terminated or aligned. This can lead to undefined behavior, including buffer overruns." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Cast from char* to wchar_t*\nThis rule indicates a potentially incorrect cast from an byte string (`char *`) to a wide-character string (`wchar_t *`).\n\nThis cast might yield strings that are not correctly terminated; including potential buffer overruns when using such strings with some dangerous APIs.\n\n\n## Recommendation\nDo not explicitly cast byte strings to wide-character strings.\n\nFor string literals, prepend the literal string with the letter \"L\" to indicate that the string is a wide-character string (`wchar_t *`).\n\nFor converting a byte literal to a wide-character string literal, you would need to use the appropriate conversion function for the platform you are using. Please see the references section for options according to your platform.\n\n\n## Example\nIn the following example, an byte string literal (`\"a\"`) is cast to a wide-character string.\n\n\n```cpp\nwchar_t* pSrc;\n\npSrc = (wchar_t*)\"a\"; // casting a byte-string literal \"a\" to a wide-character string\n```\nTo fix this issue, prepend the literal with the letter \"L\" (`L\"a\"`) to define it as a wide-character string.\n\n\n## References\n* General resources: [std::mbstowcs](https://en.cppreference.com/w/cpp/string/multibyte/mbstowcs)\n* Microsoft specific resources: [Security Considerations: International Features](https://docs.microsoft.com/en-us/windows/desktop/Intl/security-considerations--international-features)\n* Common Weakness Enumeration: [CWE-704](https://cwe.mitre.org/data/definitions/704.html).\n", + "markdown" : "# Cast from char* to wchar_t*\nThis rule indicates a potentially incorrect cast from an byte string (`char *`) to a wide-character string (`wchar_t *`).\n\nThis cast might yield strings that are not correctly terminated; including potential buffer overruns when using such strings with some dangerous APIs.\n\n\n## Recommendation\nDo not explicitly cast byte strings to wide-character strings.\n\nFor string literals, prepend the literal string with the letter \"L\" to indicate that the string is a wide-character string (`wchar_t *`).\n\nFor converting a byte literal to a wide-character string literal, you would need to use the appropriate conversion function for the platform you are using. Please see the references section for options according to your platform.\n\n\n## Example\nIn the following example, an byte string literal (`\"a\"`) is cast to a wide-character string.\n\n\n```cpp\nwchar_t* pSrc;\n\npSrc = (wchar_t*)\"a\"; // casting a byte-string literal \"a\" to a wide-character string\n```\nTo fix this issue, prepend the literal with the letter \"L\" (`L\"a\"`) to define it as a wide-character string.\n\n\n## References\n* General resources: [std::mbstowcs](https://en.cppreference.com/w/cpp/string/multibyte/mbstowcs)\n* Microsoft specific resources: [Security Considerations: International Features](https://docs.microsoft.com/en-us/windows/desktop/Intl/security-considerations--international-features)\n* Common Weakness Enumeration: [CWE-704](https://cwe.mitre.org/data/definitions/704.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-704" ], + "description" : "Casting a byte string to a wide-character string is likely\n to yield a string that is incorrectly terminated or aligned.\n This can lead to undefined behavior, including buffer overruns.", + "id" : "cpp/incorrect-string-type-conversion", + "kind" : "problem", + "name" : "Cast from char* to wchar_t*", + "precision" : "high", + "problem.severity" : "error", + "security-severity" : "8.8" + } + }, { + "id" : "cpp/very-likely-overrunning-write", + "name" : "cpp/very-likely-overrunning-write", + "shortDescription" : { + "text" : "Likely overrunning write" + }, + "fullDescription" : { + "text" : "Buffer write operations that do not control the length of data written may overflow" + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Likely overrunning write\nThe program performs a buffer copy or write operation with no upper limit on the size of the copy. By analyzing the bounds of the expressions involved, it appears that certain inputs will cause a buffer overflow to occur in this case. In addition to causing program instability, techniques exist which may allow an attacker to use this vulnerability to execute arbitrary code.\n\n\n## Recommendation\nAlways control the length of buffer copy and buffer write operations. `strncpy` should be used over `strcpy`, `snprintf` over `sprintf`, and in other cases 'n-variant' functions should be preferred.\n\n\n## Example\n\n```c\nint sayHello(uint32_t userId)\n{\n\tchar buffer[17];\n\n\tif (userId > 9999) return USER_ID_OUT_OF_BOUNDS;\n\n\t// BAD: this message overflows the buffer if userId >= 1000,\n\t// as no space for the null terminator was accounted for\n\tsprintf(buffer, \"Hello, user %d!\", userId);\n\n\tMessageBox(hWnd, buffer, \"New Message\", MB_OK);\n\t\n\treturn SUCCESS;\n}\n```\nIn this example, the call to `sprintf` writes a message of 14 characters (including the terminating null) plus the length of the string conversion of \\`userId\\` into a buffer with space for just 17 characters. While \\`userId\\` is checked to occupy no more than 4 characters when converted, there is no space in the buffer for the terminating null character if \\`userId >= 1000\\`. In this case, the null character overflows the buffer resulting in undefined behavior.\n\nTo fix this issue these changes should be made:\n\n* Control the size of the buffer by declaring it with a compile time constant.\n* Preferably, replace the call to `sprintf` with `snprintf`, using the defined constant size of the buffer or \\`sizeof(buffer)\\` as maximum length to write. This will prevent the buffer overflow.\n* Increasing the buffer size to account for the full range of \\`userId\\` and the terminating null character.\n\n## References\n* CERT C Coding Standard: [STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator](https://www.securecoding.cert.org/confluence/display/c/STR31-C.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator).\n* CERT C++ Coding Standard: [STR50-CPP. Guarantee that storage for strings has sufficient space for character data and the null terminator](https://www.securecoding.cert.org/confluence/display/cplusplus/STR50-CPP.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator).\n* Common Weakness Enumeration: [CWE-120](https://cwe.mitre.org/data/definitions/120.html).\n* Common Weakness Enumeration: [CWE-787](https://cwe.mitre.org/data/definitions/787.html).\n* Common Weakness Enumeration: [CWE-805](https://cwe.mitre.org/data/definitions/805.html).\n", + "markdown" : "# Likely overrunning write\nThe program performs a buffer copy or write operation with no upper limit on the size of the copy. By analyzing the bounds of the expressions involved, it appears that certain inputs will cause a buffer overflow to occur in this case. In addition to causing program instability, techniques exist which may allow an attacker to use this vulnerability to execute arbitrary code.\n\n\n## Recommendation\nAlways control the length of buffer copy and buffer write operations. `strncpy` should be used over `strcpy`, `snprintf` over `sprintf`, and in other cases 'n-variant' functions should be preferred.\n\n\n## Example\n\n```c\nint sayHello(uint32_t userId)\n{\n\tchar buffer[17];\n\n\tif (userId > 9999) return USER_ID_OUT_OF_BOUNDS;\n\n\t// BAD: this message overflows the buffer if userId >= 1000,\n\t// as no space for the null terminator was accounted for\n\tsprintf(buffer, \"Hello, user %d!\", userId);\n\n\tMessageBox(hWnd, buffer, \"New Message\", MB_OK);\n\t\n\treturn SUCCESS;\n}\n```\nIn this example, the call to `sprintf` writes a message of 14 characters (including the terminating null) plus the length of the string conversion of \\`userId\\` into a buffer with space for just 17 characters. While \\`userId\\` is checked to occupy no more than 4 characters when converted, there is no space in the buffer for the terminating null character if \\`userId >= 1000\\`. In this case, the null character overflows the buffer resulting in undefined behavior.\n\nTo fix this issue these changes should be made:\n\n* Control the size of the buffer by declaring it with a compile time constant.\n* Preferably, replace the call to `sprintf` with `snprintf`, using the defined constant size of the buffer or \\`sizeof(buffer)\\` as maximum length to write. This will prevent the buffer overflow.\n* Increasing the buffer size to account for the full range of \\`userId\\` and the terminating null character.\n\n## References\n* CERT C Coding Standard: [STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator](https://www.securecoding.cert.org/confluence/display/c/STR31-C.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator).\n* CERT C++ Coding Standard: [STR50-CPP. Guarantee that storage for strings has sufficient space for character data and the null terminator](https://www.securecoding.cert.org/confluence/display/cplusplus/STR50-CPP.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator).\n* Common Weakness Enumeration: [CWE-120](https://cwe.mitre.org/data/definitions/120.html).\n* Common Weakness Enumeration: [CWE-787](https://cwe.mitre.org/data/definitions/787.html).\n* Common Weakness Enumeration: [CWE-805](https://cwe.mitre.org/data/definitions/805.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "security", "external/cwe/cwe-120", "external/cwe/cwe-787", "external/cwe/cwe-805" ], + "description" : "Buffer write operations that do not control the length\n of data written may overflow", + "id" : "cpp/very-likely-overrunning-write", + "kind" : "problem", + "name" : "Likely overrunning write", + "precision" : "high", + "problem.severity" : "error", + "security-severity" : "9.3" + } + }, { + "id" : "cpp/badly-bounded-write", + "name" : "cpp/badly-bounded-write", + "shortDescription" : { + "text" : "Badly bounded write" + }, + "fullDescription" : { + "text" : "Buffer write operations with a length parameter that does not match the size of the destination buffer may overflow." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Badly bounded write\nThe program performs a buffer copy or write operation with an incorrect upper limit on the size of the copy. A sufficiently long input will overflow the target buffer. In addition to causing program instability, techniques exist which may allow an attacker to use this vulnerability to execute arbitrary code.\n\n\n## Recommendation\nUse preprocessor defines to specify the size of buffers, and use the same defines as arguments to `strncpy`, `snprintf` etc. This technique will ensure that buffer sizes are always specified correctly so that no overflow occurs.\n\n\n## Example\n\n```c\nvoid congratulateUser(const char *userName)\n{\n\tchar buffer[80];\n\n\t// BAD: even though snprintf is used, this could overflow the buffer\n\t// because the size specified is too large.\n\tsnprintf(buffer, 256, \"Congratulations, %s!\", userName);\n\n\tMessageBox(hWnd, buffer, \"New Message\", MB_OK);\n}\n```\nIn this example, the developer has used `snprintf` to control the maximum number of characters that can be written to `buffer`. Unfortunately, perhaps due to modifications since the code was first written, a limited buffer overrun can still occur because the size argument to `snprintf` is larger than the actual size of the buffer.\n\nTo fix the problem, either the second argument to `snprintf` should be changed to 80, or the buffer extended to 256 characters. A further improvement is to use a preprocessor define so that the size is only specified in one place, potentially preventing future recurrence of this issue.\n\n\n## References\n* CERT C Coding Standard: [STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator](https://www.securecoding.cert.org/confluence/display/c/STR31-C.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator).\n* CERT C++ Coding Standard: [STR50-CPP. Guarantee that storage for strings has sufficient space for character data and the null terminator](https://www.securecoding.cert.org/confluence/display/cplusplus/STR50-CPP.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator).\n* Common Weakness Enumeration: [CWE-120](https://cwe.mitre.org/data/definitions/120.html).\n* Common Weakness Enumeration: [CWE-787](https://cwe.mitre.org/data/definitions/787.html).\n* Common Weakness Enumeration: [CWE-805](https://cwe.mitre.org/data/definitions/805.html).\n", + "markdown" : "# Badly bounded write\nThe program performs a buffer copy or write operation with an incorrect upper limit on the size of the copy. A sufficiently long input will overflow the target buffer. In addition to causing program instability, techniques exist which may allow an attacker to use this vulnerability to execute arbitrary code.\n\n\n## Recommendation\nUse preprocessor defines to specify the size of buffers, and use the same defines as arguments to `strncpy`, `snprintf` etc. This technique will ensure that buffer sizes are always specified correctly so that no overflow occurs.\n\n\n## Example\n\n```c\nvoid congratulateUser(const char *userName)\n{\n\tchar buffer[80];\n\n\t// BAD: even though snprintf is used, this could overflow the buffer\n\t// because the size specified is too large.\n\tsnprintf(buffer, 256, \"Congratulations, %s!\", userName);\n\n\tMessageBox(hWnd, buffer, \"New Message\", MB_OK);\n}\n```\nIn this example, the developer has used `snprintf` to control the maximum number of characters that can be written to `buffer`. Unfortunately, perhaps due to modifications since the code was first written, a limited buffer overrun can still occur because the size argument to `snprintf` is larger than the actual size of the buffer.\n\nTo fix the problem, either the second argument to `snprintf` should be changed to 80, or the buffer extended to 256 characters. A further improvement is to use a preprocessor define so that the size is only specified in one place, potentially preventing future recurrence of this issue.\n\n\n## References\n* CERT C Coding Standard: [STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator](https://www.securecoding.cert.org/confluence/display/c/STR31-C.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator).\n* CERT C++ Coding Standard: [STR50-CPP. Guarantee that storage for strings has sufficient space for character data and the null terminator](https://www.securecoding.cert.org/confluence/display/cplusplus/STR50-CPP.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator).\n* Common Weakness Enumeration: [CWE-120](https://cwe.mitre.org/data/definitions/120.html).\n* Common Weakness Enumeration: [CWE-787](https://cwe.mitre.org/data/definitions/787.html).\n* Common Weakness Enumeration: [CWE-805](https://cwe.mitre.org/data/definitions/805.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "security", "external/cwe/cwe-120", "external/cwe/cwe-787", "external/cwe/cwe-805" ], + "description" : "Buffer write operations with a length parameter that\n does not match the size of the destination buffer may\n overflow.", + "id" : "cpp/badly-bounded-write", + "kind" : "problem", + "name" : "Badly bounded write", + "precision" : "high", + "problem.severity" : "error", + "security-severity" : "9.3" + } + }, { + "id" : "cpp/hresult-boolean-conversion", + "name" : "cpp/hresult-boolean-conversion", + "shortDescription" : { + "text" : "Cast between HRESULT and a Boolean type" + }, + "fullDescription" : { + "text" : "Casting an HRESULT to/from a Boolean type and then using it in a test expression will yield an incorrect result because success (S_OK) in HRESULT is indicated by a value of 0." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Cast between HRESULT and a Boolean type\nThis query indicates that an `HRESULT` is being cast to a Boolean type or vice versa.\n\nThe typical success value (`S_OK`) of an `HRESULT` equals 0. However, 0 indicates failure for a Boolean type.\n\nCasting an `HRESULT` to a Boolean type and then using it in a test expression will yield an incorrect result.\n\n\n## Recommendation\nTo check if a call that returns an `HRESULT` succeeded use the `FAILED` macro.\n\n\n## Example\nIn the following example, `HRESULT` is used in a test expression incorrectly as it may yield an incorrect result.\n\n\n```cpp\nLPMALLOC pMalloc;\nHRESULT hr = CoGetMalloc(1, &pMalloc);\n\nif (!hr)\n{\n // code ...\n}\n\n```\nTo fix this issue, use the `FAILED` macro in the test expression.\n\n\n## References\n* Common Weakness Enumeration: [CWE-253](https://cwe.mitre.org/data/definitions/253.html).\n", + "markdown" : "# Cast between HRESULT and a Boolean type\nThis query indicates that an `HRESULT` is being cast to a Boolean type or vice versa.\n\nThe typical success value (`S_OK`) of an `HRESULT` equals 0. However, 0 indicates failure for a Boolean type.\n\nCasting an `HRESULT` to a Boolean type and then using it in a test expression will yield an incorrect result.\n\n\n## Recommendation\nTo check if a call that returns an `HRESULT` succeeded use the `FAILED` macro.\n\n\n## Example\nIn the following example, `HRESULT` is used in a test expression incorrectly as it may yield an incorrect result.\n\n\n```cpp\nLPMALLOC pMalloc;\nHRESULT hr = CoGetMalloc(1, &pMalloc);\n\nif (!hr)\n{\n // code ...\n}\n\n```\nTo fix this issue, use the `FAILED` macro in the test expression.\n\n\n## References\n* Common Weakness Enumeration: [CWE-253](https://cwe.mitre.org/data/definitions/253.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-253" ], + "description" : "Casting an HRESULT to/from a Boolean type and then using it in a test expression will yield an incorrect result because success (S_OK) in HRESULT is indicated by a value of 0.", + "id" : "cpp/hresult-boolean-conversion", + "kind" : "problem", + "name" : "Cast between HRESULT and a Boolean type", + "precision" : "high", + "problem.severity" : "error", + "security-severity" : "7.5" + } + }, { + "id" : "cpp/system-data-exposure", + "name" : "cpp/system-data-exposure", + "shortDescription" : { + "text" : "Exposure of system data to an unauthorized control sphere" + }, + "fullDescription" : { + "text" : "Exposing system data or debugging information helps a malicious user learn about the system and form an attack plan." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Exposure of system data to an unauthorized control sphere\nExposing system data or debugging information may help a malicious user learn about the system and form an attack plan. An attacker can use error messages that reveal technologies, operating systems, and product versions to tune their attack against known vulnerabilities in the software.\n\nThis query finds locations where system configuration information might be revealed to a remote user.\n\n\n## Recommendation\nDo not expose system configuration information to remote users. Be wary of the difference between information that could be helpful to users, and unnecessary details that could be useful to a malicious user.\n\n\n## Example\nIn this example the value of the `PATH` environment variable is revealed in full to the user when a particular error occurs. This might reveal information such as the software installed on your system to a malicious user who does not have legitimate access to that information.\n\n\n```cpp\nchar* path = getenv(\"PATH\");\n\n//...\n\nsprintf(buffer, \"Cannot find exe on path: %s\", path);\nsend(socket, buffer, strlen(buffer), 0);\n\n```\nThe message should be rephrased without this information, for example:\n\n\n```cpp\nchar* path = getenv(\"PATH\");\n\n//...\n\nmessage = \"An internal error has occurred. Please try again or contact a system administrator.\\n\";\nsend(socket, message, strlen(message), 0);\n```\n\n## References\n* Common Weakness Enumeration: [CWE-497](https://cwe.mitre.org/data/definitions/497.html).\n", + "markdown" : "# Exposure of system data to an unauthorized control sphere\nExposing system data or debugging information may help a malicious user learn about the system and form an attack plan. An attacker can use error messages that reveal technologies, operating systems, and product versions to tune their attack against known vulnerabilities in the software.\n\nThis query finds locations where system configuration information might be revealed to a remote user.\n\n\n## Recommendation\nDo not expose system configuration information to remote users. Be wary of the difference between information that could be helpful to users, and unnecessary details that could be useful to a malicious user.\n\n\n## Example\nIn this example the value of the `PATH` environment variable is revealed in full to the user when a particular error occurs. This might reveal information such as the software installed on your system to a malicious user who does not have legitimate access to that information.\n\n\n```cpp\nchar* path = getenv(\"PATH\");\n\n//...\n\nsprintf(buffer, \"Cannot find exe on path: %s\", path);\nsend(socket, buffer, strlen(buffer), 0);\n\n```\nThe message should be rephrased without this information, for example:\n\n\n```cpp\nchar* path = getenv(\"PATH\");\n\n//...\n\nmessage = \"An internal error has occurred. Please try again or contact a system administrator.\\n\";\nsend(socket, message, strlen(message), 0);\n```\n\n## References\n* Common Weakness Enumeration: [CWE-497](https://cwe.mitre.org/data/definitions/497.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-497" ], + "description" : "Exposing system data or debugging information helps\n a malicious user learn about the system and form an\n attack plan.", + "id" : "cpp/system-data-exposure", + "kind" : "path-problem", + "name" : "Exposure of system data to an unauthorized control sphere", + "precision" : "high", + "problem.severity" : "warning", + "security-severity" : "6.5" + } + }, { + "id" : "cpp/cleartext-storage-file", + "name" : "cpp/cleartext-storage-file", + "shortDescription" : { + "text" : "Cleartext storage of sensitive information in file" + }, + "fullDescription" : { + "text" : "Storing sensitive information in cleartext can expose it to an attacker." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Cleartext storage of sensitive information in file\nSensitive information that is stored unencrypted is accessible to an attacker who gains access to the storage.\n\n\n## Recommendation\nEnsure that sensitive information is always encrypted before being stored to a file or transmitted over the network. It may be wise to encrypt information before it is put into a buffer that may be readable in memory.\n\nIn general, decrypt sensitive information only at the point where it is necessary for it to be used in cleartext.\n\n\n## Example\nThe following example shows two ways of storing user credentials in a file. In the 'BAD' case, the credentials are simply stored in cleartext. In the 'GOOD' case, the credentials are encrypted before storing them.\n\n\n```c\nvoid writeCredentials() {\n char *password = \"cleartext password\";\n FILE* file = fopen(\"credentials.txt\", \"w\");\n \n // BAD: write password to disk in cleartext\n fputs(password, file);\n \n // GOOD: encrypt password first\n char *encrypted = encrypt(password);\n fputs(encrypted, file);\n}\n\n\n```\n\n## References\n* M. Dowd, J. McDonald and J. Schuhm, *The Art of Software Security Assessment*, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.\n* M. Howard and D. LeBlanc, *Writing Secure Code*, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.\n* Common Weakness Enumeration: [CWE-260](https://cwe.mitre.org/data/definitions/260.html).\n* Common Weakness Enumeration: [CWE-313](https://cwe.mitre.org/data/definitions/313.html).\n", + "markdown" : "# Cleartext storage of sensitive information in file\nSensitive information that is stored unencrypted is accessible to an attacker who gains access to the storage.\n\n\n## Recommendation\nEnsure that sensitive information is always encrypted before being stored to a file or transmitted over the network. It may be wise to encrypt information before it is put into a buffer that may be readable in memory.\n\nIn general, decrypt sensitive information only at the point where it is necessary for it to be used in cleartext.\n\n\n## Example\nThe following example shows two ways of storing user credentials in a file. In the 'BAD' case, the credentials are simply stored in cleartext. In the 'GOOD' case, the credentials are encrypted before storing them.\n\n\n```c\nvoid writeCredentials() {\n char *password = \"cleartext password\";\n FILE* file = fopen(\"credentials.txt\", \"w\");\n \n // BAD: write password to disk in cleartext\n fputs(password, file);\n \n // GOOD: encrypt password first\n char *encrypted = encrypt(password);\n fputs(encrypted, file);\n}\n\n\n```\n\n## References\n* M. Dowd, J. McDonald and J. Schuhm, *The Art of Software Security Assessment*, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.\n* M. Howard and D. LeBlanc, *Writing Secure Code*, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.\n* Common Weakness Enumeration: [CWE-260](https://cwe.mitre.org/data/definitions/260.html).\n* Common Weakness Enumeration: [CWE-313](https://cwe.mitre.org/data/definitions/313.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-260", "external/cwe/cwe-313" ], + "description" : "Storing sensitive information in cleartext can expose it\n to an attacker.", + "id" : "cpp/cleartext-storage-file", + "kind" : "path-problem", + "name" : "Cleartext storage of sensitive information in file", + "precision" : "high", + "problem.severity" : "warning", + "security-severity" : "7.5" + } + }, { + "id" : "cpp/cleartext-transmission", + "name" : "cpp/cleartext-transmission", + "shortDescription" : { + "text" : "Cleartext transmission of sensitive information" + }, + "fullDescription" : { + "text" : "Transmitting sensitive information across a network in cleartext can expose it to an attacker." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Cleartext transmission of sensitive information\nSensitive information that is stored unencrypted is accessible to an attacker who gains access to the storage.\n\n\n## Recommendation\nEnsure that sensitive information is always encrypted before being stored to a file or transmitted over the network. It may be wise to encrypt information before it is put into a buffer that may be readable in memory.\n\nIn general, decrypt sensitive information only at the point where it is necessary for it to be used in cleartext.\n\n\n## Example\nThe following example shows two ways of storing user credentials in a file. In the 'BAD' case, the credentials are simply stored in cleartext. In the 'GOOD' case, the credentials are encrypted before storing them.\n\n\n```c\nvoid writeCredentials() {\n char *password = \"cleartext password\";\n FILE* file = fopen(\"credentials.txt\", \"w\");\n \n // BAD: write password to disk in cleartext\n fputs(password, file);\n \n // GOOD: encrypt password first\n char *encrypted = encrypt(password);\n fputs(encrypted, file);\n}\n\n\n```\n\n## References\n* M. Dowd, J. McDonald and J. Schuhm, *The Art of Software Security Assessment*, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.\n* M. Howard and D. LeBlanc, *Writing Secure Code*, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.\n* Common Weakness Enumeration: [CWE-319](https://cwe.mitre.org/data/definitions/319.html).\n* Common Weakness Enumeration: [CWE-359](https://cwe.mitre.org/data/definitions/359.html).\n", + "markdown" : "# Cleartext transmission of sensitive information\nSensitive information that is stored unencrypted is accessible to an attacker who gains access to the storage.\n\n\n## Recommendation\nEnsure that sensitive information is always encrypted before being stored to a file or transmitted over the network. It may be wise to encrypt information before it is put into a buffer that may be readable in memory.\n\nIn general, decrypt sensitive information only at the point where it is necessary for it to be used in cleartext.\n\n\n## Example\nThe following example shows two ways of storing user credentials in a file. In the 'BAD' case, the credentials are simply stored in cleartext. In the 'GOOD' case, the credentials are encrypted before storing them.\n\n\n```c\nvoid writeCredentials() {\n char *password = \"cleartext password\";\n FILE* file = fopen(\"credentials.txt\", \"w\");\n \n // BAD: write password to disk in cleartext\n fputs(password, file);\n \n // GOOD: encrypt password first\n char *encrypted = encrypt(password);\n fputs(encrypted, file);\n}\n\n\n```\n\n## References\n* M. Dowd, J. McDonald and J. Schuhm, *The Art of Software Security Assessment*, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.\n* M. Howard and D. LeBlanc, *Writing Secure Code*, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.\n* Common Weakness Enumeration: [CWE-319](https://cwe.mitre.org/data/definitions/319.html).\n* Common Weakness Enumeration: [CWE-359](https://cwe.mitre.org/data/definitions/359.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-319", "external/cwe/cwe-359" ], + "description" : "Transmitting sensitive information across a network in\n cleartext can expose it to an attacker.", + "id" : "cpp/cleartext-transmission", + "kind" : "path-problem", + "name" : "Cleartext transmission of sensitive information", + "precision" : "high", + "problem.severity" : "warning", + "security-severity" : "7.5" + } + }, { + "id" : "cpp/no-space-for-terminator", + "name" : "cpp/no-space-for-terminator", + "shortDescription" : { + "text" : "No space for zero terminator" + }, + "fullDescription" : { + "text" : "Allocating a buffer using 'malloc' without ensuring that there is always space for the entire string and a zero terminator can cause a buffer overrun." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# No space for zero terminator\nThis rule identifies calls to `malloc` that call `strlen` to determine the required buffer size, but do not allocate space for the zero terminator.\n\n\n## Recommendation\nThe expression highlighted by this rule creates a buffer that is of insufficient size to contain the data being copied. This makes the code vulnerable to buffer overflow which can result in anything from a segmentation fault to a security vulnerability (particularly if the array is on stack-allocated memory).\n\nIncrease the size of the buffer being allocated by one or replace `malloc`, `strcpy` pairs with a call to `strdup`\n\n\n## Example\n\n```c\n\nvoid flawed_strdup(const char *input)\n{\n\tchar *copy;\n\n\t/* Fail to allocate space for terminating '\\0' */\n\tcopy = (char *)malloc(strlen(input));\n\tstrcpy(copy, input);\n\treturn copy;\n}\n\n\n```\n\n## References\n* CERT C Coding Standard: [MEM35-C. Allocate sufficient memory for an object](https://www.securecoding.cert.org/confluence/display/c/MEM35-C.+Allocate+sufficient+memory+for+an+object).\n* Common Weakness Enumeration: [CWE-131](https://cwe.mitre.org/data/definitions/131.html).\n* Common Weakness Enumeration: [CWE-120](https://cwe.mitre.org/data/definitions/120.html).\n* Common Weakness Enumeration: [CWE-122](https://cwe.mitre.org/data/definitions/122.html).\n", + "markdown" : "# No space for zero terminator\nThis rule identifies calls to `malloc` that call `strlen` to determine the required buffer size, but do not allocate space for the zero terminator.\n\n\n## Recommendation\nThe expression highlighted by this rule creates a buffer that is of insufficient size to contain the data being copied. This makes the code vulnerable to buffer overflow which can result in anything from a segmentation fault to a security vulnerability (particularly if the array is on stack-allocated memory).\n\nIncrease the size of the buffer being allocated by one or replace `malloc`, `strcpy` pairs with a call to `strdup`\n\n\n## Example\n\n```c\n\nvoid flawed_strdup(const char *input)\n{\n\tchar *copy;\n\n\t/* Fail to allocate space for terminating '\\0' */\n\tcopy = (char *)malloc(strlen(input));\n\tstrcpy(copy, input);\n\treturn copy;\n}\n\n\n```\n\n## References\n* CERT C Coding Standard: [MEM35-C. Allocate sufficient memory for an object](https://www.securecoding.cert.org/confluence/display/c/MEM35-C.+Allocate+sufficient+memory+for+an+object).\n* Common Weakness Enumeration: [CWE-131](https://cwe.mitre.org/data/definitions/131.html).\n* Common Weakness Enumeration: [CWE-120](https://cwe.mitre.org/data/definitions/120.html).\n* Common Weakness Enumeration: [CWE-122](https://cwe.mitre.org/data/definitions/122.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "security", "external/cwe/cwe-131", "external/cwe/cwe-120", "external/cwe/cwe-122" ], + "description" : "Allocating a buffer using 'malloc' without ensuring that\n there is always space for the entire string and a zero\n terminator can cause a buffer overrun.", + "id" : "cpp/no-space-for-terminator", + "kind" : "problem", + "name" : "No space for zero terminator", + "precision" : "high", + "problem.severity" : "error", + "security-severity" : "9.8" + } + }, { + "id" : "cpp/non-https-url", + "name" : "cpp/non-https-url", + "shortDescription" : { + "text" : "Failure to use HTTPS URLs" + }, + "fullDescription" : { + "text" : "Non-HTTPS connections can be intercepted by third parties." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Failure to use HTTPS URLs\nConstructing URLs with the HTTP protocol can lead to unsecured connections.\n\n\n## Recommendation\nWhen you construct a URL, ensure that you use an HTTPS URL rather than an HTTP URL. Then, any connections that are made using that URL are secure SSL connections.\n\n\n## Example\nThe following example shows two ways of opening a connection using a URL. When the connection is opened using an HTTP URL rather than an HTTPS URL, the connection is unsecured. When the connection is opened using an HTTPS URL, the connection is a secure SSL connection.\n\n\n```cpp\n\nvoid openUrl(char *url)\n{\n\t// ...\n}\n\nopenUrl(\"http://example.com\"); // BAD\n\nopenUrl(\"https://example.com\"); // GOOD: Opening a connection to a URL using HTTPS enforces SSL.\n\n```\n\n## References\n* OWASP: [Transport Layer Protection Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Protection_Cheat_Sheet.html).\n* OWASP Top 10: [A08:2021 - Software and Data Integrity Failures](https://owasp.org/Top10/A08_2021-Software_and_Data_Integrity_Failures/).\n* Common Weakness Enumeration: [CWE-319](https://cwe.mitre.org/data/definitions/319.html).\n* Common Weakness Enumeration: [CWE-345](https://cwe.mitre.org/data/definitions/345.html).\n", + "markdown" : "# Failure to use HTTPS URLs\nConstructing URLs with the HTTP protocol can lead to unsecured connections.\n\n\n## Recommendation\nWhen you construct a URL, ensure that you use an HTTPS URL rather than an HTTP URL. Then, any connections that are made using that URL are secure SSL connections.\n\n\n## Example\nThe following example shows two ways of opening a connection using a URL. When the connection is opened using an HTTP URL rather than an HTTPS URL, the connection is unsecured. When the connection is opened using an HTTPS URL, the connection is a secure SSL connection.\n\n\n```cpp\n\nvoid openUrl(char *url)\n{\n\t// ...\n}\n\nopenUrl(\"http://example.com\"); // BAD\n\nopenUrl(\"https://example.com\"); // GOOD: Opening a connection to a URL using HTTPS enforces SSL.\n\n```\n\n## References\n* OWASP: [Transport Layer Protection Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Protection_Cheat_Sheet.html).\n* OWASP Top 10: [A08:2021 - Software and Data Integrity Failures](https://owasp.org/Top10/A08_2021-Software_and_Data_Integrity_Failures/).\n* Common Weakness Enumeration: [CWE-319](https://cwe.mitre.org/data/definitions/319.html).\n* Common Weakness Enumeration: [CWE-345](https://cwe.mitre.org/data/definitions/345.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-319", "external/cwe/cwe-345" ], + "description" : "Non-HTTPS connections can be intercepted by third parties.", + "id" : "cpp/non-https-url", + "kind" : "path-problem", + "name" : "Failure to use HTTPS URLs", + "precision" : "high", + "problem.severity" : "warning", + "security-severity" : "8.1" + } + }, { + "id" : "cpp/sql-injection", + "name" : "cpp/sql-injection", + "shortDescription" : { + "text" : "Uncontrolled data in SQL query" + }, + "fullDescription" : { + "text" : "Including user-supplied data in a SQL query without neutralizing special elements can make code vulnerable to SQL Injection." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Uncontrolled data in SQL query\nThe code passes user input as part of a SQL query without escaping special elements. It generates a SQL query using `sprintf`, with the user-supplied data directly passed as an argument to `sprintf`. This leaves the code vulnerable to attack by SQL Injection.\n\n\n## Recommendation\nUse a library routine to escape characters in the user-supplied string before converting it to SQL.\n\n\n## Example\n\n```c\nint main(int argc, char** argv) {\n char *userName = argv[2];\n \n // BAD\n char query1[1000] = {0};\n sprintf(query1, \"SELECT UID FROM USERS where name = \\\"%s\\\"\", userName);\n runSql(query1);\n \n // GOOD\n char userNameSql[1000] = {0};\n encodeSqlString(userNameSql, 1000, userName); \n char query2[1000] = {0};\n sprintf(query2, \"SELECT UID FROM USERS where name = \\\"%s\\\"\", userNameSql);\n runSql(query2);\n}\n\n```\n\n## References\n* MSDN Library: [SQL Injection](https://docs.microsoft.com/en-us/sql/relational-databases/security/sql-injection).\n* Common Weakness Enumeration: [CWE-89](https://cwe.mitre.org/data/definitions/89.html).\n", + "markdown" : "# Uncontrolled data in SQL query\nThe code passes user input as part of a SQL query without escaping special elements. It generates a SQL query using `sprintf`, with the user-supplied data directly passed as an argument to `sprintf`. This leaves the code vulnerable to attack by SQL Injection.\n\n\n## Recommendation\nUse a library routine to escape characters in the user-supplied string before converting it to SQL.\n\n\n## Example\n\n```c\nint main(int argc, char** argv) {\n char *userName = argv[2];\n \n // BAD\n char query1[1000] = {0};\n sprintf(query1, \"SELECT UID FROM USERS where name = \\\"%s\\\"\", userName);\n runSql(query1);\n \n // GOOD\n char userNameSql[1000] = {0};\n encodeSqlString(userNameSql, 1000, userName); \n char query2[1000] = {0};\n sprintf(query2, \"SELECT UID FROM USERS where name = \\\"%s\\\"\", userNameSql);\n runSql(query2);\n}\n\n```\n\n## References\n* MSDN Library: [SQL Injection](https://docs.microsoft.com/en-us/sql/relational-databases/security/sql-injection).\n* Common Weakness Enumeration: [CWE-89](https://cwe.mitre.org/data/definitions/89.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-089" ], + "description" : "Including user-supplied data in a SQL query without\n neutralizing special elements can make code vulnerable\n to SQL Injection.", + "id" : "cpp/sql-injection", + "kind" : "path-problem", + "name" : "Uncontrolled data in SQL query", + "precision" : "high", + "problem.severity" : "error", + "security-severity" : "8.8" + } + }, { + "id" : "cpp/tainted-format-string", + "name" : "cpp/tainted-format-string", + "shortDescription" : { + "text" : "Uncontrolled format string" + }, + "fullDescription" : { + "text" : "Using externally-controlled format strings in printf-style functions can lead to buffer overflows or data representation problems." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Uncontrolled format string\nThe program uses input from the user as a format string for `printf` style functions. This can lead to buffer overflows or data representation problems. An attacker can exploit this weakness to crash the program, disclose information or even execute arbitrary code.\n\nThe results of this rule do not include inputs from the user that are transferred through global variables. Those can be found in the related rule \"Uncontrolled format string (through global variable)\".\n\n\n## Recommendation\nUse constant expressions as the format strings. If you need to print a value from the user, use `printf(\"%s\", value_from_user)`.\n\n\n## Example\n\n```c\n#include \n\nvoid printWrapper(char *str) {\n\tprintf(str);\n}\n\nint main(int argc, char **argv) {\n\t// This should be avoided\n\tprintf(argv[1]);\n\n\t// This should be avoided too, because it has the same effect\n\tprintWrapper(argv[1]);\n\n\t// This is fine\n\tprintf(\"%s\", argv[1]);\n}\n```\n\n## References\n* CERT C Coding Standard: [FIO30-C. Exclude user input from format strings](https://www.securecoding.cert.org/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings).\n* Common Weakness Enumeration: [CWE-134](https://cwe.mitre.org/data/definitions/134.html).\n", + "markdown" : "# Uncontrolled format string\nThe program uses input from the user as a format string for `printf` style functions. This can lead to buffer overflows or data representation problems. An attacker can exploit this weakness to crash the program, disclose information or even execute arbitrary code.\n\nThe results of this rule do not include inputs from the user that are transferred through global variables. Those can be found in the related rule \"Uncontrolled format string (through global variable)\".\n\n\n## Recommendation\nUse constant expressions as the format strings. If you need to print a value from the user, use `printf(\"%s\", value_from_user)`.\n\n\n## Example\n\n```c\n#include \n\nvoid printWrapper(char *str) {\n\tprintf(str);\n}\n\nint main(int argc, char **argv) {\n\t// This should be avoided\n\tprintf(argv[1]);\n\n\t// This should be avoided too, because it has the same effect\n\tprintWrapper(argv[1]);\n\n\t// This is fine\n\tprintf(\"%s\", argv[1]);\n}\n```\n\n## References\n* CERT C Coding Standard: [FIO30-C. Exclude user input from format strings](https://www.securecoding.cert.org/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings).\n* Common Weakness Enumeration: [CWE-134](https://cwe.mitre.org/data/definitions/134.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "security", "external/cwe/cwe-134" ], + "description" : "Using externally-controlled format strings in\n printf-style functions can lead to buffer overflows\n or data representation problems.", + "id" : "cpp/tainted-format-string", + "kind" : "path-problem", + "name" : "Uncontrolled format string", + "precision" : "high", + "problem.severity" : "warning", + "security-severity" : "9.3" + } + }, { + "id" : "cpp/use-of-string-after-lifetime-ends", + "name" : "cpp/use-of-string-after-lifetime-ends", + "shortDescription" : { + "text" : "Use of string after lifetime ends" + }, + "fullDescription" : { + "text" : "If the value of a call to 'c_str' outlives the underlying object it may lead to unexpected behavior." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Use of string after lifetime ends\nCalling `c_str` on a `std::string` object returns a pointer to the underlying character array. When the `std::string` object is destroyed, the pointer returned by `c_str` is no longer valid. If the pointer is used after the `std::string` object is destroyed, then the behavior is undefined.\n\n\n## Recommendation\nEnsure that the pointer returned by `c_str` does not outlive the underlying `std::string` object.\n\n\n## Example\nThe following example concatenates two `std::string` objects, and then converts the resulting string to a C string using `c_str` so that it can be passed to the `work` function. However, the underlying `std::string` object that represents the concatenated string is destroyed as soon as the call to `c_str` returns. This means that `work` is given a pointer to invalid memory.\n\n\n```cpp\n#include \nvoid work(const char*);\n\n// BAD: the concatenated string is deallocated when `c_str` returns. So `work`\n// is given a pointer to invalid memory.\nvoid work_with_combined_string_bad(std::string s1, std::string s2) {\n const char* combined_string = (s1 + s2).c_str();\n work(combined_string);\n}\n```\nThe following example fixes the above code by ensuring that the pointer returned by the call to `c_str` does not outlive the underlying `std::string` objects. This ensures that the pointer passed to `work` points to valid memory.\n\n\n```cpp\n#include \nvoid work(const char*);\n\n// GOOD: the concatenated string outlives the call to `work`. So the pointer\n// obtainted from `c_str` is valid.\nvoid work_with_combined_string_good(std::string s1, std::string s2) {\n auto combined_string = s1 + s2;\n work(combined_string.c_str());\n}\n```\n\n## References\n* [MEM50-CPP. Do not access freed memory](https://wiki.sei.cmu.edu/confluence/display/cplusplus/MEM50-CPP.+Do+not+access+freed+memory).\n* Common Weakness Enumeration: [CWE-416](https://cwe.mitre.org/data/definitions/416.html).\n* Common Weakness Enumeration: [CWE-664](https://cwe.mitre.org/data/definitions/664.html).\n", + "markdown" : "# Use of string after lifetime ends\nCalling `c_str` on a `std::string` object returns a pointer to the underlying character array. When the `std::string` object is destroyed, the pointer returned by `c_str` is no longer valid. If the pointer is used after the `std::string` object is destroyed, then the behavior is undefined.\n\n\n## Recommendation\nEnsure that the pointer returned by `c_str` does not outlive the underlying `std::string` object.\n\n\n## Example\nThe following example concatenates two `std::string` objects, and then converts the resulting string to a C string using `c_str` so that it can be passed to the `work` function. However, the underlying `std::string` object that represents the concatenated string is destroyed as soon as the call to `c_str` returns. This means that `work` is given a pointer to invalid memory.\n\n\n```cpp\n#include \nvoid work(const char*);\n\n// BAD: the concatenated string is deallocated when `c_str` returns. So `work`\n// is given a pointer to invalid memory.\nvoid work_with_combined_string_bad(std::string s1, std::string s2) {\n const char* combined_string = (s1 + s2).c_str();\n work(combined_string);\n}\n```\nThe following example fixes the above code by ensuring that the pointer returned by the call to `c_str` does not outlive the underlying `std::string` objects. This ensures that the pointer passed to `work` points to valid memory.\n\n\n```cpp\n#include \nvoid work(const char*);\n\n// GOOD: the concatenated string outlives the call to `work`. So the pointer\n// obtainted from `c_str` is valid.\nvoid work_with_combined_string_good(std::string s1, std::string s2) {\n auto combined_string = s1 + s2;\n work(combined_string.c_str());\n}\n```\n\n## References\n* [MEM50-CPP. Do not access freed memory](https://wiki.sei.cmu.edu/confluence/display/cplusplus/MEM50-CPP.+Do+not+access+freed+memory).\n* Common Weakness Enumeration: [CWE-416](https://cwe.mitre.org/data/definitions/416.html).\n* Common Weakness Enumeration: [CWE-664](https://cwe.mitre.org/data/definitions/664.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "security", "external/cwe/cwe-416", "external/cwe/cwe-664" ], + "description" : "If the value of a call to 'c_str' outlives the underlying object it may lead to unexpected behavior.", + "id" : "cpp/use-of-string-after-lifetime-ends", + "kind" : "problem", + "name" : "Use of string after lifetime ends", + "precision" : "high", + "problem.severity" : "warning", + "security-severity" : "8.8" + } + }, { + "id" : "cpp/comparison-with-wider-type", + "name" : "cpp/comparison-with-wider-type", + "shortDescription" : { + "text" : "Comparison of narrow type with wide type in loop condition" + }, + "fullDescription" : { + "text" : "Comparisons between types of different widths in a loop condition can cause the loop to behave unexpectedly." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Comparison of narrow type with wide type in loop condition\nIn a loop condition, comparison of a value of a narrow type with a value of a wide type may result in unexpected behavior if the wider value is sufficiently large (or small). This is because the narrower value may overflow. This can lead to an infinite loop.\n\n\n## Recommendation\nChange the types of the compared values so that the value on the narrower side of the comparison is at least as wide as the value it is being compared with.\n\n\n## Example\nIn this example, `bytes_received` is compared against `max_get` in a `while` loop. However, `bytes_received` is an `int16_t`, and `max_get` is an `int32_t`. Because `max_get` is larger than `INT16_MAX`, the loop condition is always `true`, so the loop never terminates.\n\nThis problem is avoided in the 'GOOD' case because `bytes_received2` is an `int32_t`, which is as wide as the type of `max_get`.\n\n\n```c\nvoid main(int argc, char **argv) {\n\tuint32_t big_num = INT32_MAX;\n\tchar buf[big_num];\n\tint16_t bytes_received = 0;\n\tint max_get = INT16_MAX + 1;\n\n\t// BAD: 'bytes_received' is compared with a value of a wider type.\n\t// 'bytes_received' overflows before reaching 'max_get',\n\t// causing an infinite loop\n\twhile (bytes_received < max_get)\n\t\tbytes_received += get_from_input(buf, bytes_received);\n\t}\n\n\tuint32_t bytes_received = 0;\n\n\t// GOOD: 'bytes_received2' has a type at least as wide as 'max_get'\n\twhile (bytes_received < max_get) {\n\t\tbytes_received += get_from_input(buf, bytes_received);\n\t}\n\n}\n\n\nint getFromInput(char *buf, short pos) {\n\t// write to buf\n\t// ...\n\treturn 1;\n}\n\n```\n\n## References\n* [Data type ranges](https://docs.microsoft.com/en-us/cpp/cpp/data-type-ranges)\n* [INT18-C. Evaluate integer expressions in a larger size before comparing or assigning to that size ](https://wiki.sei.cmu.edu/confluence/display/c/INT18-C.+Evaluate+integer+expressions+in+a+larger+size+before+comparing+or+assigning+to+that+size)\n* Common Weakness Enumeration: [CWE-190](https://cwe.mitre.org/data/definitions/190.html).\n* Common Weakness Enumeration: [CWE-197](https://cwe.mitre.org/data/definitions/197.html).\n* Common Weakness Enumeration: [CWE-835](https://cwe.mitre.org/data/definitions/835.html).\n", + "markdown" : "# Comparison of narrow type with wide type in loop condition\nIn a loop condition, comparison of a value of a narrow type with a value of a wide type may result in unexpected behavior if the wider value is sufficiently large (or small). This is because the narrower value may overflow. This can lead to an infinite loop.\n\n\n## Recommendation\nChange the types of the compared values so that the value on the narrower side of the comparison is at least as wide as the value it is being compared with.\n\n\n## Example\nIn this example, `bytes_received` is compared against `max_get` in a `while` loop. However, `bytes_received` is an `int16_t`, and `max_get` is an `int32_t`. Because `max_get` is larger than `INT16_MAX`, the loop condition is always `true`, so the loop never terminates.\n\nThis problem is avoided in the 'GOOD' case because `bytes_received2` is an `int32_t`, which is as wide as the type of `max_get`.\n\n\n```c\nvoid main(int argc, char **argv) {\n\tuint32_t big_num = INT32_MAX;\n\tchar buf[big_num];\n\tint16_t bytes_received = 0;\n\tint max_get = INT16_MAX + 1;\n\n\t// BAD: 'bytes_received' is compared with a value of a wider type.\n\t// 'bytes_received' overflows before reaching 'max_get',\n\t// causing an infinite loop\n\twhile (bytes_received < max_get)\n\t\tbytes_received += get_from_input(buf, bytes_received);\n\t}\n\n\tuint32_t bytes_received = 0;\n\n\t// GOOD: 'bytes_received2' has a type at least as wide as 'max_get'\n\twhile (bytes_received < max_get) {\n\t\tbytes_received += get_from_input(buf, bytes_received);\n\t}\n\n}\n\n\nint getFromInput(char *buf, short pos) {\n\t// write to buf\n\t// ...\n\treturn 1;\n}\n\n```\n\n## References\n* [Data type ranges](https://docs.microsoft.com/en-us/cpp/cpp/data-type-ranges)\n* [INT18-C. Evaluate integer expressions in a larger size before comparing or assigning to that size ](https://wiki.sei.cmu.edu/confluence/display/c/INT18-C.+Evaluate+integer+expressions+in+a+larger+size+before+comparing+or+assigning+to+that+size)\n* Common Weakness Enumeration: [CWE-190](https://cwe.mitre.org/data/definitions/190.html).\n* Common Weakness Enumeration: [CWE-197](https://cwe.mitre.org/data/definitions/197.html).\n* Common Weakness Enumeration: [CWE-835](https://cwe.mitre.org/data/definitions/835.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "security", "external/cwe/cwe-190", "external/cwe/cwe-197", "external/cwe/cwe-835" ], + "description" : "Comparisons between types of different widths in a loop\n condition can cause the loop to behave unexpectedly.", + "id" : "cpp/comparison-with-wider-type", + "kind" : "problem", + "name" : "Comparison of narrow type with wide type in loop condition", + "precision" : "high", + "problem.severity" : "warning", + "security-severity" : "7.8" + } + }, { + "id" : "cpp/uncontrolled-arithmetic", + "name" : "cpp/uncontrolled-arithmetic", + "shortDescription" : { + "text" : "Uncontrolled data in arithmetic expression" + }, + "fullDescription" : { + "text" : "Arithmetic operations on uncontrolled data that is not validated can cause overflows." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Uncontrolled data in arithmetic expression\nPerforming calculations on uncontrolled data can result in integer overflows unless the input is validated.\n\nIf the data is not under your control, and can take extremely large values, even arithmetic operations that would usually result in a small change in magnitude may result in overflows.\n\n\n## Recommendation\nAlways guard against overflow in arithmetic operations on uncontrolled data by doing one of the following:\n\n* Validate the data.\n* Define a guard on the arithmetic expression, so that the operation is performed only if the result can be known to be less than, or equal to, the maximum value for the type, for example `INT_MAX`.\n* Use a wider type, so that larger input values do not cause overflow.\n\n## Example\nIn this example, a random integer is generated. Because the value is not controlled by the programmer, it could be extremely large. Performing arithmetic operations on this value could therefore cause an overflow. To avoid this happening, the example shows how to perform a check before performing an arithmetic operation.\n\n\n```c\nint main(int argc, char** argv) {\n\tint i = rand();\n\t// BAD: potential overflow\n\tint j = i + 1000;\n\n\t// ...\n\n\tint n = rand();\n\tint k;\n\t// GOOD: use a guard to prevent overflow\n\tif (n < INT_MAX-1000)\n\t\tk = n + 1000;\n\telse\n\t\tk = INT_MAX;\n}\n\n```\n\n## References\n* Common Weakness Enumeration: [CWE-190](https://cwe.mitre.org/data/definitions/190.html).\n* Common Weakness Enumeration: [CWE-191](https://cwe.mitre.org/data/definitions/191.html).\n", + "markdown" : "# Uncontrolled data in arithmetic expression\nPerforming calculations on uncontrolled data can result in integer overflows unless the input is validated.\n\nIf the data is not under your control, and can take extremely large values, even arithmetic operations that would usually result in a small change in magnitude may result in overflows.\n\n\n## Recommendation\nAlways guard against overflow in arithmetic operations on uncontrolled data by doing one of the following:\n\n* Validate the data.\n* Define a guard on the arithmetic expression, so that the operation is performed only if the result can be known to be less than, or equal to, the maximum value for the type, for example `INT_MAX`.\n* Use a wider type, so that larger input values do not cause overflow.\n\n## Example\nIn this example, a random integer is generated. Because the value is not controlled by the programmer, it could be extremely large. Performing arithmetic operations on this value could therefore cause an overflow. To avoid this happening, the example shows how to perform a check before performing an arithmetic operation.\n\n\n```c\nint main(int argc, char** argv) {\n\tint i = rand();\n\t// BAD: potential overflow\n\tint j = i + 1000;\n\n\t// ...\n\n\tint n = rand();\n\tint k;\n\t// GOOD: use a guard to prevent overflow\n\tif (n < INT_MAX-1000)\n\t\tk = n + 1000;\n\telse\n\t\tk = INT_MAX;\n}\n\n```\n\n## References\n* Common Weakness Enumeration: [CWE-190](https://cwe.mitre.org/data/definitions/190.html).\n* Common Weakness Enumeration: [CWE-191](https://cwe.mitre.org/data/definitions/191.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-190", "external/cwe/cwe-191" ], + "description" : "Arithmetic operations on uncontrolled data that is not\n validated can cause overflows.", + "id" : "cpp/uncontrolled-arithmetic", + "kind" : "path-problem", + "name" : "Uncontrolled data in arithmetic expression", + "precision" : "high", + "problem.severity" : "warning", + "security-severity" : "8.6" + } + }, { + "id" : "cpp/weak-cryptographic-algorithm", + "name" : "cpp/weak-cryptographic-algorithm", + "shortDescription" : { + "text" : "Use of a broken or risky cryptographic algorithm" + }, + "fullDescription" : { + "text" : "Using broken or weak cryptographic algorithms can allow an attacker to compromise security." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Use of a broken or risky cryptographic algorithm\nUsing broken or weak cryptographic algorithms can leave data vulnerable to being decrypted.\n\nMany cryptographic algorithms provided by cryptography libraries are known to be weak, or flawed. Using such an algorithm means that an attacker may be able to easily decrypt the encrypted data.\n\n\n## Recommendation\nEnsure that you use a strong, modern cryptographic algorithm. Use at least AES-128 or RSA-2048.\n\n\n## Example\nThe following code shows an example of using the `advapi` windows API to decrypt some data. When creating a key, you must specify which algorithm to use. The first example uses DES which is an older algorithm that is now considered weak. The second example uses AES, which is a strong modern algorithm.\n\n\n```c\nvoid advapi() {\n HCRYPTPROV hCryptProv;\n HCRYPTKEY hKey;\n HCRYPTHASH hHash;\n // other preparation goes here\n\n // BAD: use 3DES for key\n CryptDeriveKey(hCryptProv, CALG_3DES, hHash, 0, &hKey);\n\n // GOOD: use AES\n CryptDeriveKey(hCryptProv, CALG_AES_256, hHash, 0, &hKey);\n}\n\n\n```\n\n## References\n* NIST, FIPS 140 Annex a: [ Approved Security Functions](http://csrc.nist.gov/publications/fips/fips140-2/fips1402annexa.pdf).\n* NIST, SP 800-131A: [ Transitions: Recommendation for Transitioning the Use of Cryptographic Algorithms and Key Lengths](http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar1.pdf).\n* Common Weakness Enumeration: [CWE-327](https://cwe.mitre.org/data/definitions/327.html).\n", + "markdown" : "# Use of a broken or risky cryptographic algorithm\nUsing broken or weak cryptographic algorithms can leave data vulnerable to being decrypted.\n\nMany cryptographic algorithms provided by cryptography libraries are known to be weak, or flawed. Using such an algorithm means that an attacker may be able to easily decrypt the encrypted data.\n\n\n## Recommendation\nEnsure that you use a strong, modern cryptographic algorithm. Use at least AES-128 or RSA-2048.\n\n\n## Example\nThe following code shows an example of using the `advapi` windows API to decrypt some data. When creating a key, you must specify which algorithm to use. The first example uses DES which is an older algorithm that is now considered weak. The second example uses AES, which is a strong modern algorithm.\n\n\n```c\nvoid advapi() {\n HCRYPTPROV hCryptProv;\n HCRYPTKEY hKey;\n HCRYPTHASH hHash;\n // other preparation goes here\n\n // BAD: use 3DES for key\n CryptDeriveKey(hCryptProv, CALG_3DES, hHash, 0, &hKey);\n\n // GOOD: use AES\n CryptDeriveKey(hCryptProv, CALG_AES_256, hHash, 0, &hKey);\n}\n\n\n```\n\n## References\n* NIST, FIPS 140 Annex a: [ Approved Security Functions](http://csrc.nist.gov/publications/fips/fips140-2/fips1402annexa.pdf).\n* NIST, SP 800-131A: [ Transitions: Recommendation for Transitioning the Use of Cryptographic Algorithms and Key Lengths](http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar1.pdf).\n* Common Weakness Enumeration: [CWE-327](https://cwe.mitre.org/data/definitions/327.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-327" ], + "description" : "Using broken or weak cryptographic algorithms can allow\n an attacker to compromise security.", + "id" : "cpp/weak-cryptographic-algorithm", + "kind" : "problem", + "name" : "Use of a broken or risky cryptographic algorithm", + "precision" : "high", + "problem.severity" : "error", + "security-severity" : "7.5" + } + }, { + "id" : "cpp/openssl-heartbleed", + "name" : "cpp/openssl-heartbleed", + "shortDescription" : { + "text" : "Use of a version of OpenSSL with Heartbleed" + }, + "fullDescription" : { + "text" : "Using an old version of OpenSSL can allow remote attackers to retrieve portions of memory." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Use of a version of OpenSSL with Heartbleed\nEarlier versions of the popular OpenSSL library suffer from a buffer overflow in its \"heartbeat\" code. Because of the location of the problematic code, this vulnerability is often called \"Heartbleed\".\n\nSoftware that includes a copy of OpenSSL should be sure to use a current version of the library. If it uses an older version, it will be vulnerable to any network site it connects with.\n\n\n## Recommendation\nUpgrade to the latest version of OpenSSL. This problem was fixed in version 1.0.1g.\n\n\n## Example\nThe following code is present in earlier versions of OpenSSL. The `payload` variable is the number of bytes that should be copied from the request back into the response. The call to `memcpy` does this copy. The problem is that `payload` is supplied as part of the remote request, and there is no code that checks the size of it. If the caller supplies a very large value, then the `memcpy` call will copy memory that is outside the request packet.\n\n\n```c\nint\ntls1_process_heartbeat(SSL *s)\n {\n unsigned char *p = &s->s3->rrec.data[0], *pl;\n unsigned short hbtype;\n unsigned int payload;\n \n /* ... */\n \n hbtype = *p++;\n n2s(p, payload);\n pl = p;\n \n /* ... */\n \n if (hbtype == TLS1_HB_REQUEST)\n {\n /* ... */\n memcpy(bp, pl, payload); // BAD: overflow here\n /* ... */\n }\n \n \n /* ... */\n \n }\n\n```\n\n## References\n* Common Vulnerabilities and Exposures: [CVE-2014-0160](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-0160).\n* OpenSSL News: [OpenSSL Security Advisory \\[07 Apr 2014\\]](https://www.openssl.org/news/secadv_20140407.txt).\n* Common Weakness Enumeration: [CWE-327](https://cwe.mitre.org/data/definitions/327.html).\n* Common Weakness Enumeration: [CWE-788](https://cwe.mitre.org/data/definitions/788.html).\n", + "markdown" : "# Use of a version of OpenSSL with Heartbleed\nEarlier versions of the popular OpenSSL library suffer from a buffer overflow in its \"heartbeat\" code. Because of the location of the problematic code, this vulnerability is often called \"Heartbleed\".\n\nSoftware that includes a copy of OpenSSL should be sure to use a current version of the library. If it uses an older version, it will be vulnerable to any network site it connects with.\n\n\n## Recommendation\nUpgrade to the latest version of OpenSSL. This problem was fixed in version 1.0.1g.\n\n\n## Example\nThe following code is present in earlier versions of OpenSSL. The `payload` variable is the number of bytes that should be copied from the request back into the response. The call to `memcpy` does this copy. The problem is that `payload` is supplied as part of the remote request, and there is no code that checks the size of it. If the caller supplies a very large value, then the `memcpy` call will copy memory that is outside the request packet.\n\n\n```c\nint\ntls1_process_heartbeat(SSL *s)\n {\n unsigned char *p = &s->s3->rrec.data[0], *pl;\n unsigned short hbtype;\n unsigned int payload;\n \n /* ... */\n \n hbtype = *p++;\n n2s(p, payload);\n pl = p;\n \n /* ... */\n \n if (hbtype == TLS1_HB_REQUEST)\n {\n /* ... */\n memcpy(bp, pl, payload); // BAD: overflow here\n /* ... */\n }\n \n \n /* ... */\n \n }\n\n```\n\n## References\n* Common Vulnerabilities and Exposures: [CVE-2014-0160](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-0160).\n* OpenSSL News: [OpenSSL Security Advisory \\[07 Apr 2014\\]](https://www.openssl.org/news/secadv_20140407.txt).\n* Common Weakness Enumeration: [CWE-327](https://cwe.mitre.org/data/definitions/327.html).\n* Common Weakness Enumeration: [CWE-788](https://cwe.mitre.org/data/definitions/788.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-327", "external/cwe/cwe-788" ], + "description" : "Using an old version of OpenSSL can allow remote\n attackers to retrieve portions of memory.", + "id" : "cpp/openssl-heartbleed", + "kind" : "problem", + "name" : "Use of a version of OpenSSL with Heartbleed", + "precision" : "very-high", + "problem.severity" : "error", + "security-severity" : "7.5" + } + }, { + "id" : "cpp/toctou-race-condition", + "name" : "cpp/toctou-race-condition", + "shortDescription" : { + "text" : "Time-of-check time-of-use filesystem race condition" + }, + "fullDescription" : { + "text" : "Separately checking the state of a file before operating on it may allow an attacker to modify the file between the two operations." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Time-of-check time-of-use filesystem race condition\nOften it is necessary to check the state of a file before using it. These checks usually take a file name to be checked, and if the check returns positively, then the file is opened or otherwise operated upon.\n\nHowever, in the time between the check and the operation, the underlying file referenced by the file name could be changed by an attacker, causing unexpected behavior.\n\n\n## Recommendation\nWherever possible, use functions that operate on file descriptors rather than file names (for example, `fchmod` rather than `chmod`).\n\nFor access checks, you can temporarily change the UID and GID to that of the user whose permissions are being checked, and then perform the operation. This has the effect of \"atomically\" combining a permissions check with the operation.\n\nIf file-system locking tools are available on your platform, then locking the file before the check can prevent an unexpected update. However, note that on some platforms (for example, Unix) file-system locks are typically *advisory*, and so can be ignored by an attacker.\n\n\n## Example\nThe following example shows a case where a file is opened and then, if the opening was successful, its permissions are changed with `chmod`. However, an attacker might change the target of the file name between the initial opening and the permissions change, potentially changing the permissions of a different file.\n\n\n```c\nchar *file_name;\nFILE *f_ptr;\n \n/* Initialize file_name */\n \nf_ptr = fopen(file_name, \"w\");\nif (f_ptr == NULL) {\n /* Handle error */\n}\n \n/* ... */\n \nif (chmod(file_name, S_IRUSR) == -1) {\n /* Handle error */\n}\n```\nThis can be avoided by using `fchmod` with the file descriptor that was received from opening the file. This ensures that the permissions change is applied to the very same file that was opened.\n\n\n```c\nchar *file_name;\nint fd;\n \n/* Initialize file_name */\n \nfd = open(\n file_name,\n O_WRONLY | O_CREAT | O_EXCL,\n S_IRWXU\n);\nif (fd == -1) {\n /* Handle error */\n}\n \n/* ... */\n \nif (fchmod(fd, S_IRUSR) == -1) {\n /* Handle error */\n}\n```\n\n## References\n* The CERT Oracle Secure Coding Standard for C: [ FIO01-C. Be careful using functions that use file names for identification ](https://www.securecoding.cert.org/confluence/display/c/FIO01-C.+Be+careful+using+functions+that+use+file+names+for+identification).\n* Common Weakness Enumeration: [CWE-367](https://cwe.mitre.org/data/definitions/367.html).\n", + "markdown" : "# Time-of-check time-of-use filesystem race condition\nOften it is necessary to check the state of a file before using it. These checks usually take a file name to be checked, and if the check returns positively, then the file is opened or otherwise operated upon.\n\nHowever, in the time between the check and the operation, the underlying file referenced by the file name could be changed by an attacker, causing unexpected behavior.\n\n\n## Recommendation\nWherever possible, use functions that operate on file descriptors rather than file names (for example, `fchmod` rather than `chmod`).\n\nFor access checks, you can temporarily change the UID and GID to that of the user whose permissions are being checked, and then perform the operation. This has the effect of \"atomically\" combining a permissions check with the operation.\n\nIf file-system locking tools are available on your platform, then locking the file before the check can prevent an unexpected update. However, note that on some platforms (for example, Unix) file-system locks are typically *advisory*, and so can be ignored by an attacker.\n\n\n## Example\nThe following example shows a case where a file is opened and then, if the opening was successful, its permissions are changed with `chmod`. However, an attacker might change the target of the file name between the initial opening and the permissions change, potentially changing the permissions of a different file.\n\n\n```c\nchar *file_name;\nFILE *f_ptr;\n \n/* Initialize file_name */\n \nf_ptr = fopen(file_name, \"w\");\nif (f_ptr == NULL) {\n /* Handle error */\n}\n \n/* ... */\n \nif (chmod(file_name, S_IRUSR) == -1) {\n /* Handle error */\n}\n```\nThis can be avoided by using `fchmod` with the file descriptor that was received from opening the file. This ensures that the permissions change is applied to the very same file that was opened.\n\n\n```c\nchar *file_name;\nint fd;\n \n/* Initialize file_name */\n \nfd = open(\n file_name,\n O_WRONLY | O_CREAT | O_EXCL,\n S_IRWXU\n);\nif (fd == -1) {\n /* Handle error */\n}\n \n/* ... */\n \nif (fchmod(fd, S_IRUSR) == -1) {\n /* Handle error */\n}\n```\n\n## References\n* The CERT Oracle Secure Coding Standard for C: [ FIO01-C. Be careful using functions that use file names for identification ](https://www.securecoding.cert.org/confluence/display/c/FIO01-C.+Be+careful+using+functions+that+use+file+names+for+identification).\n* Common Weakness Enumeration: [CWE-367](https://cwe.mitre.org/data/definitions/367.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-367" ], + "description" : "Separately checking the state of a file before operating\n on it may allow an attacker to modify the file between\n the two operations.", + "id" : "cpp/toctou-race-condition", + "kind" : "problem", + "name" : "Time-of-check time-of-use filesystem race condition", + "precision" : "high", + "problem.severity" : "warning", + "security-severity" : "7.7" + } + }, { + "id" : "cpp/open-call-with-mode-argument", + "name" : "cpp/open-call-with-mode-argument", + "shortDescription" : { + "text" : "File opened with O_CREAT flag but without mode argument" + }, + "fullDescription" : { + "text" : "Opening a file with the O_CREAT flag but without mode argument reads arbitrary bytes from the stack." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# File opened with O_CREAT flag but without mode argument\nWhen opening a file with the `O_CREAT` or `O_TMPFILE` flag, the `mode` must be supplied. If the `mode` argument is omitted, some arbitrary bytes from the stack will be used as the file mode. This leaks some bits from the stack into the permissions of the file.\n\n\n## Recommendation\nThe `mode` must be supplied when `O_CREAT` or `O_TMPFILE` is specified.\n\n\n## Example\nThe first example opens a file with the `O_CREAT` flag without supplying the `mode` argument. In this case arbitrary bytes from the stack will be used as `mode` argument. The second example correctly supplies the `mode` argument and creates a file that is user readable and writable.\n\n\n```c\nint open_file_bad() {\n\t// BAD - this uses arbitrary bytes from the stack as mode argument\n return open(FILE, O_CREAT)\n}\n\nint open_file_good() {\n\t// GOOD - the mode argument is supplied\n return open(FILE, O_CREAT, S_IRUSR | S_IWUSR)\n}\n\n```\n", + "markdown" : "# File opened with O_CREAT flag but without mode argument\nWhen opening a file with the `O_CREAT` or `O_TMPFILE` flag, the `mode` must be supplied. If the `mode` argument is omitted, some arbitrary bytes from the stack will be used as the file mode. This leaks some bits from the stack into the permissions of the file.\n\n\n## Recommendation\nThe `mode` must be supplied when `O_CREAT` or `O_TMPFILE` is specified.\n\n\n## Example\nThe first example opens a file with the `O_CREAT` flag without supplying the `mode` argument. In this case arbitrary bytes from the stack will be used as `mode` argument. The second example correctly supplies the `mode` argument and creates a file that is user readable and writable.\n\n\n```c\nint open_file_bad() {\n\t// BAD - this uses arbitrary bytes from the stack as mode argument\n return open(FILE, O_CREAT)\n}\n\nint open_file_good() {\n\t// GOOD - the mode argument is supplied\n return open(FILE, O_CREAT, S_IRUSR | S_IWUSR)\n}\n\n```\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-732" ], + "description" : "Opening a file with the O_CREAT flag but without mode argument reads arbitrary bytes from the stack.", + "id" : "cpp/open-call-with-mode-argument", + "kind" : "problem", + "name" : "File opened with O_CREAT flag but without mode argument", + "precision" : "high", + "problem.severity" : "error", + "security-severity" : "7.8" + } + }, { + "id" : "cpp/unsafe-dacl-security-descriptor", + "name" : "cpp/unsafe-dacl-security-descriptor", + "shortDescription" : { + "text" : "Setting a DACL to NULL in a SECURITY_DESCRIPTOR" + }, + "fullDescription" : { + "text" : "Setting a DACL to NULL in a SECURITY_DESCRIPTOR will result in an unprotected object. If the DACL that belongs to the security descriptor of an object is set to NULL, a null DACL is created. A null DACL grants full access to any user who requests it; normal security checking is not performed with respect to the object." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Setting a DACL to NULL in a SECURITY_DESCRIPTOR\nThis query indicates that a call is setting the DACL field in a `SECURITY_DESCRIPTOR` to null.\n\nWhen using `SetSecurityDescriptorDacl` to set a discretionary access control (DACL), setting the `bDaclPresent` argument to `TRUE` indicates the presence of a DACL in the security description in the argument `pDacl`.\n\nWhen the `pDacl` parameter does not point to a DACL (i.e. it is `NULL`) and the `bDaclPresent` flag is `TRUE`, a `NULL DACL` is specified.\n\nA `NULL DACL` grants full access to any user who requests it; normal security checking is not performed with respect to the object.\n\n\n## Recommendation\nYou should not use a `NULL DACL` with an object because any user can change the DACL and owner of the security descriptor.\n\n\n## Example\nIn the following example, the call to `SetSecurityDescriptorDacl` is setting an unsafe DACL (`NULL DACL`) to the security descriptor.\n\n\n```cpp\nSECURITY_DESCRIPTOR pSD;\nSECURITY_ATTRIBUTES SA;\n\nif (!InitializeSecurityDescriptor(&pSD, SECURITY_DESCRIPTOR_REVISION))\n{\n // error handling\n}\nif (!SetSecurityDescriptorDacl(&pSD,\n TRUE, // bDaclPresent - this value indicates the presence of a DACL in the security descriptor\n NULL, // pDacl - the pDacl parameter does not point to a DACL. All access will be allowed\n FALSE))\n{\n // error handling\n}\n\n```\nTo fix this issue, `pDacl` argument should be a pointer to an `ACL` structure that specifies the DACL for the security descriptor.\n\n\n## References\n* [SetSecurityDescriptorDacl function (Microsoft documentation).](https://docs.microsoft.com/en-us/windows/desktop/api/securitybaseapi/nf-securitybaseapi-setsecuritydescriptordacl)\n* Common Weakness Enumeration: [CWE-732](https://cwe.mitre.org/data/definitions/732.html).\n", + "markdown" : "# Setting a DACL to NULL in a SECURITY_DESCRIPTOR\nThis query indicates that a call is setting the DACL field in a `SECURITY_DESCRIPTOR` to null.\n\nWhen using `SetSecurityDescriptorDacl` to set a discretionary access control (DACL), setting the `bDaclPresent` argument to `TRUE` indicates the presence of a DACL in the security description in the argument `pDacl`.\n\nWhen the `pDacl` parameter does not point to a DACL (i.e. it is `NULL`) and the `bDaclPresent` flag is `TRUE`, a `NULL DACL` is specified.\n\nA `NULL DACL` grants full access to any user who requests it; normal security checking is not performed with respect to the object.\n\n\n## Recommendation\nYou should not use a `NULL DACL` with an object because any user can change the DACL and owner of the security descriptor.\n\n\n## Example\nIn the following example, the call to `SetSecurityDescriptorDacl` is setting an unsafe DACL (`NULL DACL`) to the security descriptor.\n\n\n```cpp\nSECURITY_DESCRIPTOR pSD;\nSECURITY_ATTRIBUTES SA;\n\nif (!InitializeSecurityDescriptor(&pSD, SECURITY_DESCRIPTOR_REVISION))\n{\n // error handling\n}\nif (!SetSecurityDescriptorDacl(&pSD,\n TRUE, // bDaclPresent - this value indicates the presence of a DACL in the security descriptor\n NULL, // pDacl - the pDacl parameter does not point to a DACL. All access will be allowed\n FALSE))\n{\n // error handling\n}\n\n```\nTo fix this issue, `pDacl` argument should be a pointer to an `ACL` structure that specifies the DACL for the security descriptor.\n\n\n## References\n* [SetSecurityDescriptorDacl function (Microsoft documentation).](https://docs.microsoft.com/en-us/windows/desktop/api/securitybaseapi/nf-securitybaseapi-setsecuritydescriptordacl)\n* Common Weakness Enumeration: [CWE-732](https://cwe.mitre.org/data/definitions/732.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-732" ], + "description" : "Setting a DACL to NULL in a SECURITY_DESCRIPTOR will result in an unprotected object.\n If the DACL that belongs to the security descriptor of an object is set to NULL, a null DACL is created.\n A null DACL grants full access to any user who requests it;\n normal security checking is not performed with respect to the object.", + "id" : "cpp/unsafe-dacl-security-descriptor", + "kind" : "problem", + "name" : "Setting a DACL to NULL in a SECURITY_DESCRIPTOR", + "precision" : "high", + "problem.severity" : "error", + "security-severity" : "7.8" + } + }, { + "id" : "cpp/dangerous-function-overflow", + "name" : "cpp/dangerous-function-overflow", + "shortDescription" : { + "text" : "Use of dangerous function" + }, + "fullDescription" : { + "text" : "Use of a standard library function that does not guard against buffer overflow." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Use of dangerous function\nThis rule finds calls to the `gets` function, which is dangerous and should not be used. See **Related rules** below for rules that identify other dangerous functions.\n\nThe `gets` function is one of the vulnerabilities exploited by the Internet Worm of 1988, one of the first computer worms to spread through the Internet. The `gets` function provides no way to limit the amount of data that is read and stored, so without prior knowledge of the input it is impossible to use it safely with any size of buffer.\n\n\n## Recommendation\nReplace calls to `gets` with `fgets`, specifying the maximum length to copy. This will prevent the buffer overflow.\n\n\n## Example\nThe following example gets a string from standard input in two ways:\n\n\n```c\n#define BUFFERSIZE (1024)\n\n// BAD: using gets\nvoid echo_bad() {\n char buffer[BUFFERSIZE];\n gets(buffer);\n printf(\"Input was: '%s'\\n\", buffer);\n}\n\n// GOOD: using fgets\nvoid echo_good() {\n char buffer[BUFFERSIZE];\n fgets(buffer, BUFFERSIZE, stdin);\n printf(\"Input was: '%s'\\n\", buffer);\n}\n\n```\nThe first version uses `gets` and will overflow if the input is longer than the buffer. The second version of the code uses `fgets` and will not overflow, because the amount of data written is limited by the length parameter.\n\n\n## Related rules\nOther dangerous functions identified by CWE-676 (\"Use of Potentially Dangerous Function\") include `strcpy` and `strcat`. Use of these functions is highlighted by rules for the following CWEs:\n\n* [CWE-120 Classic Buffer Overflow](https://cwe.mitre.org/data/definitions/120.html).\n* [CWE-131 Incorrect Calculation of Buffer Size](https://cwe.mitre.org/data/definitions/131.html).\n\n## References\n* Wikipedia: [Morris worm](http://en.wikipedia.org/wiki/Morris_worm).\n* E. Spafford. *The Internet Worm Program: An Analysis*. Purdue Technical Report CSD-TR-823, [(online)](http://www.textfiles.com/100/tr823.txt), 1988.\n* Common Weakness Enumeration: [CWE-242](https://cwe.mitre.org/data/definitions/242.html).\n* Common Weakness Enumeration: [CWE-676](https://cwe.mitre.org/data/definitions/676.html).\n", + "markdown" : "# Use of dangerous function\nThis rule finds calls to the `gets` function, which is dangerous and should not be used. See **Related rules** below for rules that identify other dangerous functions.\n\nThe `gets` function is one of the vulnerabilities exploited by the Internet Worm of 1988, one of the first computer worms to spread through the Internet. The `gets` function provides no way to limit the amount of data that is read and stored, so without prior knowledge of the input it is impossible to use it safely with any size of buffer.\n\n\n## Recommendation\nReplace calls to `gets` with `fgets`, specifying the maximum length to copy. This will prevent the buffer overflow.\n\n\n## Example\nThe following example gets a string from standard input in two ways:\n\n\n```c\n#define BUFFERSIZE (1024)\n\n// BAD: using gets\nvoid echo_bad() {\n char buffer[BUFFERSIZE];\n gets(buffer);\n printf(\"Input was: '%s'\\n\", buffer);\n}\n\n// GOOD: using fgets\nvoid echo_good() {\n char buffer[BUFFERSIZE];\n fgets(buffer, BUFFERSIZE, stdin);\n printf(\"Input was: '%s'\\n\", buffer);\n}\n\n```\nThe first version uses `gets` and will overflow if the input is longer than the buffer. The second version of the code uses `fgets` and will not overflow, because the amount of data written is limited by the length parameter.\n\n\n## Related rules\nOther dangerous functions identified by CWE-676 (\"Use of Potentially Dangerous Function\") include `strcpy` and `strcat`. Use of these functions is highlighted by rules for the following CWEs:\n\n* [CWE-120 Classic Buffer Overflow](https://cwe.mitre.org/data/definitions/120.html).\n* [CWE-131 Incorrect Calculation of Buffer Size](https://cwe.mitre.org/data/definitions/131.html).\n\n## References\n* Wikipedia: [Morris worm](http://en.wikipedia.org/wiki/Morris_worm).\n* E. Spafford. *The Internet Worm Program: An Analysis*. Purdue Technical Report CSD-TR-823, [(online)](http://www.textfiles.com/100/tr823.txt), 1988.\n* Common Weakness Enumeration: [CWE-242](https://cwe.mitre.org/data/definitions/242.html).\n* Common Weakness Enumeration: [CWE-676](https://cwe.mitre.org/data/definitions/676.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "security", "external/cwe/cwe-242", "external/cwe/cwe-676" ], + "description" : "Use of a standard library function that does not guard against buffer overflow.", + "id" : "cpp/dangerous-function-overflow", + "kind" : "problem", + "name" : "Use of dangerous function", + "precision" : "very-high", + "problem.severity" : "error", + "security-severity" : "10.0" + } + }, { + "id" : "cpp/dangerous-cin", + "name" : "cpp/dangerous-cin", + "shortDescription" : { + "text" : "Dangerous use of 'cin'" + }, + "fullDescription" : { + "text" : "Using `cin` without specifying the length of the input may be dangerous." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Dangerous use of 'cin'\nThis rule finds calls to `std::istream::operator>>` on `std::cin` without a preceding call to `cin.width`. Consuming input from `cin` without specifying the length of the input is dangerous due to the possibility of buffer overflows.\n\n\n## Recommendation\nAlways specify the length of any input expected from `cin` by calling `cin.width` before consuming the input.\n\n\n## Example\nThe following example shows both a dangerous and a safe way to consume input from `cin`.\n\n\n```cpp\n#define BUFFER_SIZE 20\n\nvoid bad()\n{\n\tchar buffer[BUFFER_SIZE];\n\t// BAD: Use of 'cin' without specifying the length of the input.\n\tcin >> buffer;\n\tbuffer[BUFFER_SIZE-1] = '\\0';\n}\n\nvoid good()\n{\n\tchar buffer[BUFFER_SIZE];\n\t// GOOD: Specifying the length of the input before using 'cin'.\n\tcin.width(BUFFER_SIZE);\n\tcin >> buffer;\n\tbuffer[BUFFER_SIZE-1] = '\\0';\n}\n\n```\n\n## References\n* Common Weakness Enumeration: [CWE-676](https://cwe.mitre.org/data/definitions/676.html).\n", + "markdown" : "# Dangerous use of 'cin'\nThis rule finds calls to `std::istream::operator>>` on `std::cin` without a preceding call to `cin.width`. Consuming input from `cin` without specifying the length of the input is dangerous due to the possibility of buffer overflows.\n\n\n## Recommendation\nAlways specify the length of any input expected from `cin` by calling `cin.width` before consuming the input.\n\n\n## Example\nThe following example shows both a dangerous and a safe way to consume input from `cin`.\n\n\n```cpp\n#define BUFFER_SIZE 20\n\nvoid bad()\n{\n\tchar buffer[BUFFER_SIZE];\n\t// BAD: Use of 'cin' without specifying the length of the input.\n\tcin >> buffer;\n\tbuffer[BUFFER_SIZE-1] = '\\0';\n}\n\nvoid good()\n{\n\tchar buffer[BUFFER_SIZE];\n\t// GOOD: Specifying the length of the input before using 'cin'.\n\tcin.width(BUFFER_SIZE);\n\tcin >> buffer;\n\tbuffer[BUFFER_SIZE-1] = '\\0';\n}\n\n```\n\n## References\n* Common Weakness Enumeration: [CWE-676](https://cwe.mitre.org/data/definitions/676.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "security", "external/cwe/cwe-676" ], + "description" : "Using `cin` without specifying the length of the input\n may be dangerous.", + "id" : "cpp/dangerous-cin", + "kind" : "problem", + "name" : "Dangerous use of 'cin'", + "precision" : "high", + "problem.severity" : "error", + "security-severity" : "10.0" + } + }, { + "id" : "cpp/suspicious-add-sizeof", + "name" : "cpp/suspicious-add-sizeof", + "shortDescription" : { + "text" : "Suspicious add with sizeof" + }, + "fullDescription" : { + "text" : "Explicitly scaled pointer arithmetic expressions can cause buffer overflow conditions if the offset is also implicitly scaled." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Suspicious add with sizeof\nPointer arithmetic in C and C++ is automatically scaled according to the size of the data type. For example, if the type of `p` is `T*` and `sizeof(T) == 4` then the expression `p+1` adds 4 bytes to `p`.\n\nThis query finds code of the form `p + k*sizeof(T)`. Such code is usually a mistake because there is no need to manually scale the offset by `sizeof(T)`.\n\n\n## Recommendation\n1. Whenever possible, use the array subscript operator rather than pointer arithmetic. For example, replace `*(p+k)` with `p[k]`.\n1. Cast to the correct type before using pointer arithmetic. For example, if the type of `p` is `char*` but it really points to an array of type `double[]` then use the syntax `(double*)p + k` to get a pointer to the `k`'th element of the array.\n\n## Example\n\n```cpp\nint example1(int i) {\n int intArray[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };\n int *intPointer = intArray;\n // BAD: the offset is already automatically scaled by sizeof(int),\n // so this code will compute the wrong offset.\n return *(intPointer + (i * sizeof(int)));\n}\n\nint example2(int i) {\n int intArray[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };\n int *intPointer = intArray;\n // GOOD: the offset is automatically scaled by sizeof(int).\n return *(intPointer + i);\n}\n\n```\n\n## References\n* Common Weakness Enumeration: [CWE-468](https://cwe.mitre.org/data/definitions/468.html).\n", + "markdown" : "# Suspicious add with sizeof\nPointer arithmetic in C and C++ is automatically scaled according to the size of the data type. For example, if the type of `p` is `T*` and `sizeof(T) == 4` then the expression `p+1` adds 4 bytes to `p`.\n\nThis query finds code of the form `p + k*sizeof(T)`. Such code is usually a mistake because there is no need to manually scale the offset by `sizeof(T)`.\n\n\n## Recommendation\n1. Whenever possible, use the array subscript operator rather than pointer arithmetic. For example, replace `*(p+k)` with `p[k]`.\n1. Cast to the correct type before using pointer arithmetic. For example, if the type of `p` is `char*` but it really points to an array of type `double[]` then use the syntax `(double*)p + k` to get a pointer to the `k`'th element of the array.\n\n## Example\n\n```cpp\nint example1(int i) {\n int intArray[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };\n int *intPointer = intArray;\n // BAD: the offset is already automatically scaled by sizeof(int),\n // so this code will compute the wrong offset.\n return *(intPointer + (i * sizeof(int)));\n}\n\nint example2(int i) {\n int intArray[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };\n int *intPointer = intArray;\n // GOOD: the offset is automatically scaled by sizeof(int).\n return *(intPointer + i);\n}\n\n```\n\n## References\n* Common Weakness Enumeration: [CWE-468](https://cwe.mitre.org/data/definitions/468.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-468" ], + "description" : "Explicitly scaled pointer arithmetic expressions\n can cause buffer overflow conditions if the offset is also\n implicitly scaled.", + "id" : "cpp/suspicious-add-sizeof", + "kind" : "problem", + "name" : "Suspicious add with sizeof", + "precision" : "high", + "problem.severity" : "warning", + "security-severity" : "8.8" + } + }, { + "id" : "cpp/cgi-xss", + "name" : "cpp/cgi-xss", + "shortDescription" : { + "text" : "CGI script vulnerable to cross-site scripting" + }, + "fullDescription" : { + "text" : "Writing user input directly to a web page allows for a cross-site scripting vulnerability." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# CGI script vulnerable to cross-site scripting\nDirectly writing an HTTP request parameter back to a web page allows for a cross-site scripting vulnerability. The data is displayed in a user's web browser as belonging to one site, but it is provided by some other site that the user browses to. In effect, such an attack allows one web site to insert content in the other one.\n\nFor web servers implemented with the Common Gateway Interface (CGI), HTTP parameters are supplied via the `QUERY_STRING` environment variable.\n\n\n## Recommendation\nTo guard against cross-site scripting, consider escaping special characters before writing the HTTP parameter back to the page.\n\n\n## Example\nIn the following example, the `bad_server` writes a parameter directly back to the HTML page that the user will see. The `good_server` first escapes any HTML special characters before writing to the HTML page.\n\n\n```c\nvoid bad_server() {\n char* query = getenv(\"QUERY_STRING\");\n puts(\"

Query results for \");\n // BAD: Printing out an HTTP parameter with no escaping\n puts(query);\n puts(\"\\n

\\n\");\n puts(do_search(query));\n}\n\nvoid good_server() {\n char* query = getenv(\"QUERY_STRING\");\n puts(\"

Query results for \");\n // GOOD: Escape HTML characters before adding to a page\n char* query_escaped = escape_html(query);\n puts(query_escaped);\n free(query_escaped);\n\n puts(\"\\n

\\n\");\n puts(do_search(query));\n}\n\n```\n\n## References\n* OWASP: [XSS (Cross Site Scripting) Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html).\n* Wikipedia: [Cross-site scripting](http://en.wikipedia.org/wiki/Cross-site_scripting).\n* IETF Tools: [The Common Gateway Specification (CGI)](http://tools.ietf.org/html/draft-robinson-www-interface-00).\n* Common Weakness Enumeration: [CWE-79](https://cwe.mitre.org/data/definitions/79.html).\n", + "markdown" : "# CGI script vulnerable to cross-site scripting\nDirectly writing an HTTP request parameter back to a web page allows for a cross-site scripting vulnerability. The data is displayed in a user's web browser as belonging to one site, but it is provided by some other site that the user browses to. In effect, such an attack allows one web site to insert content in the other one.\n\nFor web servers implemented with the Common Gateway Interface (CGI), HTTP parameters are supplied via the `QUERY_STRING` environment variable.\n\n\n## Recommendation\nTo guard against cross-site scripting, consider escaping special characters before writing the HTTP parameter back to the page.\n\n\n## Example\nIn the following example, the `bad_server` writes a parameter directly back to the HTML page that the user will see. The `good_server` first escapes any HTML special characters before writing to the HTML page.\n\n\n```c\nvoid bad_server() {\n char* query = getenv(\"QUERY_STRING\");\n puts(\"

Query results for \");\n // BAD: Printing out an HTTP parameter with no escaping\n puts(query);\n puts(\"\\n

\\n\");\n puts(do_search(query));\n}\n\nvoid good_server() {\n char* query = getenv(\"QUERY_STRING\");\n puts(\"

Query results for \");\n // GOOD: Escape HTML characters before adding to a page\n char* query_escaped = escape_html(query);\n puts(query_escaped);\n free(query_escaped);\n\n puts(\"\\n

\\n\");\n puts(do_search(query));\n}\n\n```\n\n## References\n* OWASP: [XSS (Cross Site Scripting) Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html).\n* Wikipedia: [Cross-site scripting](http://en.wikipedia.org/wiki/Cross-site_scripting).\n* IETF Tools: [The Common Gateway Specification (CGI)](http://tools.ietf.org/html/draft-robinson-www-interface-00).\n* Common Weakness Enumeration: [CWE-79](https://cwe.mitre.org/data/definitions/79.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-079" ], + "description" : "Writing user input directly to a web page\n allows for a cross-site scripting vulnerability.", + "id" : "cpp/cgi-xss", + "kind" : "path-problem", + "name" : "CGI script vulnerable to cross-site scripting", + "precision" : "high", + "problem.severity" : "error", + "security-severity" : "6.1" + } + }, { + "id" : "cpp/external-entity-expansion", + "name" : "cpp/external-entity-expansion", + "shortDescription" : { + "text" : "XML external entity expansion" + }, + "fullDescription" : { + "text" : "Parsing user-controlled XML documents and allowing expansion of external entity references may lead to disclosure of confidential data or denial of service." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# XML external entity expansion\nParsing untrusted XML files with a weakly configured XML parser may lead to an XML external entity (XXE) attack. This type of attack uses external entity references to access arbitrary files on a system, carry out denial-of-service (DoS) attacks, or server-side request forgery. Even when the result of parsing is not returned to the user, DoS attacks are still possible and out-of-band data retrieval techniques may allow attackers to steal sensitive data.\n\n\n## Recommendation\nThe easiest way to prevent XXE attacks is to disable external entity handling when parsing untrusted data. How this is done depends on the library being used. Note that some libraries, such as recent versions of `libxml`, disable entity expansion by default, so unless you have explicitly enabled entity expansion, no further action needs to be taken.\n\n\n## Example\nThe following example uses the `Xerces-C++` XML parser to parse a string `data`. If that string is from an untrusted source, this code may be vulnerable to an XXE attack, since the parser is constructed in its default state with `setDisableDefaultEntityResolution` set to `false`:\n\n\n```cpp\n\nXercesDOMParser *parser = new XercesDOMParser();\n\nparser->parse(data); // BAD (parser is not correctly configured, may expand external entity references)\n\n```\nTo guard against XXE attacks, the `setDisableDefaultEntityResolution` option should be set to `true`.\n\n\n```cpp\n\nXercesDOMParser *parser = new XercesDOMParser();\n\nparser->setDisableDefaultEntityResolution(true);\nparser->parse(data);\n\n```\n\n## References\n* OWASP: [XML External Entity (XXE) Processing](https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Processing).\n* OWASP: [XML External Entity Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html).\n* Timothy Morgen: [XML Schema, DTD, and Entity Attacks](https://research.nccgroup.com/2014/05/19/xml-schema-dtd-and-entity-attacks-a-compendium-of-known-techniques/).\n* Timur Yunusov, Alexey Osipov: [XML Out-Of-Band Data Retrieval](https://www.slideshare.net/qqlan/bh-ready-v4).\n* Common Weakness Enumeration: [CWE-611](https://cwe.mitre.org/data/definitions/611.html).\n", + "markdown" : "# XML external entity expansion\nParsing untrusted XML files with a weakly configured XML parser may lead to an XML external entity (XXE) attack. This type of attack uses external entity references to access arbitrary files on a system, carry out denial-of-service (DoS) attacks, or server-side request forgery. Even when the result of parsing is not returned to the user, DoS attacks are still possible and out-of-band data retrieval techniques may allow attackers to steal sensitive data.\n\n\n## Recommendation\nThe easiest way to prevent XXE attacks is to disable external entity handling when parsing untrusted data. How this is done depends on the library being used. Note that some libraries, such as recent versions of `libxml`, disable entity expansion by default, so unless you have explicitly enabled entity expansion, no further action needs to be taken.\n\n\n## Example\nThe following example uses the `Xerces-C++` XML parser to parse a string `data`. If that string is from an untrusted source, this code may be vulnerable to an XXE attack, since the parser is constructed in its default state with `setDisableDefaultEntityResolution` set to `false`:\n\n\n```cpp\n\nXercesDOMParser *parser = new XercesDOMParser();\n\nparser->parse(data); // BAD (parser is not correctly configured, may expand external entity references)\n\n```\nTo guard against XXE attacks, the `setDisableDefaultEntityResolution` option should be set to `true`.\n\n\n```cpp\n\nXercesDOMParser *parser = new XercesDOMParser();\n\nparser->setDisableDefaultEntityResolution(true);\nparser->parse(data);\n\n```\n\n## References\n* OWASP: [XML External Entity (XXE) Processing](https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Processing).\n* OWASP: [XML External Entity Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html).\n* Timothy Morgen: [XML Schema, DTD, and Entity Attacks](https://research.nccgroup.com/2014/05/19/xml-schema-dtd-and-entity-attacks-a-compendium-of-known-techniques/).\n* Timur Yunusov, Alexey Osipov: [XML Out-Of-Band Data Retrieval](https://www.slideshare.net/qqlan/bh-ready-v4).\n* Common Weakness Enumeration: [CWE-611](https://cwe.mitre.org/data/definitions/611.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-611" ], + "description" : "Parsing user-controlled XML documents and allowing expansion of\n external entity references may lead to disclosure of\n confidential data or denial of service.", + "id" : "cpp/external-entity-expansion", + "kind" : "path-problem", + "name" : "XML external entity expansion", + "precision" : "high", + "problem.severity" : "warning", + "security-severity" : "9.1" + } + }, { + "id" : "cpp/insufficient-key-size", + "name" : "cpp/insufficient-key-size", + "shortDescription" : { + "text" : "Use of a cryptographic algorithm with insufficient key size" + }, + "fullDescription" : { + "text" : "Using cryptographic algorithms with too small a key size can allow an attacker to compromise security." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Use of a cryptographic algorithm with insufficient key size\nUsing cryptographic algorithms with a small key size can leave data vulnerable to being decrypted.\n\nMany cryptographic algorithms provided by cryptography libraries can be configured with key sizes that are vulnerable to brute force attacks. Using such a key size means that an attacker may be able to easily decrypt the encrypted data.\n\n\n## Recommendation\nEnsure that you use a strong, modern cryptographic algorithm. Use at least AES-128 or RSA-2048.\n\n\n## Example\nThe following code shows an example of using the `openssl` library to generate an RSA key. When creating a key, you must specify which key size to use. The first example uses 1024 bits, which is not considered sufficient. The second example uses 2048 bits, which is currently considered sufficient.\n\n\n```c\nvoid encrypt_with_openssl(EVP_PKEY_CTX *ctx) {\n\n // BAD: only 1024 bits for an RSA key\n EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 1024);\n\n // GOOD: 2048 bits for an RSA key\n EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 2048);\n}\n```\n\n## References\n* NIST, FIPS 140 Annex a: [ Approved Security Functions](http://csrc.nist.gov/publications/fips/fips140-2/fips1402annexa.pdf).\n* NIST, SP 800-131A: [ Transitions: Recommendation for Transitioning the Use of Cryptographic Algorithms and Key Lengths](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf).\n* Common Weakness Enumeration: [CWE-326](https://cwe.mitre.org/data/definitions/326.html).\n", + "markdown" : "# Use of a cryptographic algorithm with insufficient key size\nUsing cryptographic algorithms with a small key size can leave data vulnerable to being decrypted.\n\nMany cryptographic algorithms provided by cryptography libraries can be configured with key sizes that are vulnerable to brute force attacks. Using such a key size means that an attacker may be able to easily decrypt the encrypted data.\n\n\n## Recommendation\nEnsure that you use a strong, modern cryptographic algorithm. Use at least AES-128 or RSA-2048.\n\n\n## Example\nThe following code shows an example of using the `openssl` library to generate an RSA key. When creating a key, you must specify which key size to use. The first example uses 1024 bits, which is not considered sufficient. The second example uses 2048 bits, which is currently considered sufficient.\n\n\n```c\nvoid encrypt_with_openssl(EVP_PKEY_CTX *ctx) {\n\n // BAD: only 1024 bits for an RSA key\n EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 1024);\n\n // GOOD: 2048 bits for an RSA key\n EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 2048);\n}\n```\n\n## References\n* NIST, FIPS 140 Annex a: [ Approved Security Functions](http://csrc.nist.gov/publications/fips/fips140-2/fips1402annexa.pdf).\n* NIST, SP 800-131A: [ Transitions: Recommendation for Transitioning the Use of Cryptographic Algorithms and Key Lengths](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf).\n* Common Weakness Enumeration: [CWE-326](https://cwe.mitre.org/data/definitions/326.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-326" ], + "description" : "Using cryptographic algorithms with too small a key size can\n allow an attacker to compromise security.", + "id" : "cpp/insufficient-key-size", + "kind" : "path-problem", + "name" : "Use of a cryptographic algorithm with insufficient key size", + "precision" : "high", + "problem.severity" : "error", + "security-severity" : "7.5" + } + }, { + "id" : "cpp/command-line-injection", + "name" : "cpp/command-line-injection", + "shortDescription" : { + "text" : "Uncontrolled data used in OS command" + }, + "fullDescription" : { + "text" : "Using user-supplied data in an OS command, without neutralizing special elements, can make code vulnerable to command injection." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Uncontrolled data used in OS command\nThe code passes user input as part of a call to `system` or `popen` without escaping special elements. It generates a command line using `sprintf`, with the user-supplied data directly passed as a formatting argument. This leaves the code vulnerable to attack by command injection.\n\n\n## Recommendation\nUse a library routine to escape characters in the user-supplied string before passing it to a command shell.\n\n\n## Example\nThe following example runs an external command in two ways. The first way uses `sprintf` to build a command directly out of a user-supplied argument. As such, it is vulnerable to command injection. The second way quotes the user-provided value before embedding it in the command; assuming the `encodeShellString` utility is correct, this code should be safe against command injection.\n\n\n```c\nint main(int argc, char** argv) {\n char *userName = argv[2];\n \n {\n // BAD: a string from the user is injected directly into\n // a command line.\n char command1[1000] = {0};\n sprintf(command1, \"userinfo -v \\\"%s\\\"\", userName);\n system(command1);\n }\n\n {\n // GOOD: the user string is encoded by a library routine.\n char userNameQuoted[1000] = {0};\n encodeShellString(userNameQuoted, 1000, userName); \n char command2[1000] = {0};\n sprintf(command2, \"userinfo -v %s\", userNameQuoted);\n system(command2);\n }\n}\n\n```\n\n## References\n* CERT C Coding Standard: [STR02-C. Sanitize data passed to complex subsystems](https://www.securecoding.cert.org/confluence/display/c/STR02-C.+Sanitize+data+passed+to+complex+subsystems).\n* OWASP: [Command Injection](https://www.owasp.org/index.php/Command_Injection).\n* Common Weakness Enumeration: [CWE-78](https://cwe.mitre.org/data/definitions/78.html).\n* Common Weakness Enumeration: [CWE-88](https://cwe.mitre.org/data/definitions/88.html).\n", + "markdown" : "# Uncontrolled data used in OS command\nThe code passes user input as part of a call to `system` or `popen` without escaping special elements. It generates a command line using `sprintf`, with the user-supplied data directly passed as a formatting argument. This leaves the code vulnerable to attack by command injection.\n\n\n## Recommendation\nUse a library routine to escape characters in the user-supplied string before passing it to a command shell.\n\n\n## Example\nThe following example runs an external command in two ways. The first way uses `sprintf` to build a command directly out of a user-supplied argument. As such, it is vulnerable to command injection. The second way quotes the user-provided value before embedding it in the command; assuming the `encodeShellString` utility is correct, this code should be safe against command injection.\n\n\n```c\nint main(int argc, char** argv) {\n char *userName = argv[2];\n \n {\n // BAD: a string from the user is injected directly into\n // a command line.\n char command1[1000] = {0};\n sprintf(command1, \"userinfo -v \\\"%s\\\"\", userName);\n system(command1);\n }\n\n {\n // GOOD: the user string is encoded by a library routine.\n char userNameQuoted[1000] = {0};\n encodeShellString(userNameQuoted, 1000, userName); \n char command2[1000] = {0};\n sprintf(command2, \"userinfo -v %s\", userNameQuoted);\n system(command2);\n }\n}\n\n```\n\n## References\n* CERT C Coding Standard: [STR02-C. Sanitize data passed to complex subsystems](https://www.securecoding.cert.org/confluence/display/c/STR02-C.+Sanitize+data+passed+to+complex+subsystems).\n* OWASP: [Command Injection](https://www.owasp.org/index.php/Command_Injection).\n* Common Weakness Enumeration: [CWE-78](https://cwe.mitre.org/data/definitions/78.html).\n* Common Weakness Enumeration: [CWE-88](https://cwe.mitre.org/data/definitions/88.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-078", "external/cwe/cwe-088" ], + "description" : "Using user-supplied data in an OS command, without\n neutralizing special elements, can make code vulnerable\n to command injection.", + "id" : "cpp/command-line-injection", + "kind" : "path-problem", + "name" : "Uncontrolled data used in OS command", + "precision" : "high", + "problem.severity" : "error", + "security-severity" : "9.8" + } + }, { + "id" : "cpp/new-free-mismatch", + "name" : "cpp/new-free-mismatch", + "shortDescription" : { + "text" : "Mismatching new/free or malloc/delete" + }, + "fullDescription" : { + "text" : "An object that was allocated with 'malloc' or 'new' is being freed using a mismatching 'free' or 'delete'." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Mismatching new/free or malloc/delete\nThis rule finds `delete` expressions whose argument is a pointer that points to memory allocated using the `malloc` function, and calls to `free` whose argument is a pointer that points to memory allocated using the `new` operator. Behavior in such cases is undefined and should be avoided.\n\n\n## Recommendation\nUse the `delete` operator when freeing memory allocated with `new`, and the `free` function when freeing memory allocated with `malloc`.\n\n\n## Example\n\n```cpp\nRecord *ptr = new Record(...);\n\n...\n\nfree(ptr); // BAD: ptr was created using 'new', but is being freed using 'free'\n\n```\n\n## References\n* isocpp.org 'Standard C++', \"[Can I free() pointers allocated with new? Can I delete pointers allocated with malloc()?](https://isocpp.org/wiki/faq/freestore-mgmt#mixing-malloc-and-delete)\"\n* Wikipedia, \"[Relation to malloc and free](https://en.wikipedia.org/wiki/New_and_delete_(C%2B%2B)#Relation_to_malloc_and_free)\" in *new and delete (C++)*.\n* Common Weakness Enumeration: [CWE-401](https://cwe.mitre.org/data/definitions/401.html).\n", + "markdown" : "# Mismatching new/free or malloc/delete\nThis rule finds `delete` expressions whose argument is a pointer that points to memory allocated using the `malloc` function, and calls to `free` whose argument is a pointer that points to memory allocated using the `new` operator. Behavior in such cases is undefined and should be avoided.\n\n\n## Recommendation\nUse the `delete` operator when freeing memory allocated with `new`, and the `free` function when freeing memory allocated with `malloc`.\n\n\n## Example\n\n```cpp\nRecord *ptr = new Record(...);\n\n...\n\nfree(ptr); // BAD: ptr was created using 'new', but is being freed using 'free'\n\n```\n\n## References\n* isocpp.org 'Standard C++', \"[Can I free() pointers allocated with new? Can I delete pointers allocated with malloc()?](https://isocpp.org/wiki/faq/freestore-mgmt#mixing-malloc-and-delete)\"\n* Wikipedia, \"[Relation to malloc and free](https://en.wikipedia.org/wiki/New_and_delete_(C%2B%2B)#Relation_to_malloc_and_free)\" in *new and delete (C++)*.\n* Common Weakness Enumeration: [CWE-401](https://cwe.mitre.org/data/definitions/401.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "security", "external/cwe/cwe-401" ], + "description" : "An object that was allocated with 'malloc' or 'new' is being freed using a mismatching 'free' or 'delete'.", + "id" : "cpp/new-free-mismatch", + "kind" : "problem", + "name" : "Mismatching new/free or malloc/delete", + "precision" : "high", + "problem.severity" : "warning", + "security-severity" : "7.5" + } + }, { + "id" : "cpp/double-free", + "name" : "cpp/double-free", + "shortDescription" : { + "text" : "Potential double free" + }, + "fullDescription" : { + "text" : "Freeing a resource more than once can lead to undefined behavior and cause memory corruption." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Potential double free\nDeallocating memory more than once can lead to a double-free vulnerability. This can be exploited to corrupt the allocator's internal data structures, which can lead to denial-of-service attacks by crashing the program, or security vulnerabilities, by allowing an attacker to overwrite arbitrary memory locations.\n\n\n## Recommendation\nEnsure that all execution paths deallocate the allocated memory at most once. If possible, reassign the pointer to a null value after deallocating it. This will prevent double-free vulnerabilities since most deallocation functions will perform a null-pointer check before attempting to deallocate the memory.\n\n\n## Example\n\n```cpp\nint* f() {\n\tint *buff = malloc(SIZE*sizeof(int));\n\tdo_stuff(buff);\n\tfree(buff);\n\tint *new_buffer = malloc(SIZE*sizeof(int));\n\tfree(buff); // BAD: If new_buffer is assigned the same address as buff,\n // the memory allocator will free the new buffer memory region,\n // leading to use-after-free problems and memory corruption.\n\treturn new_buffer;\n}\n\n```\n\n## References\n* OWASP: [Doubly freeing memory](https://owasp.org/www-community/vulnerabilities/Doubly_freeing_memory).\n* Common Weakness Enumeration: [CWE-415](https://cwe.mitre.org/data/definitions/415.html).\n", + "markdown" : "# Potential double free\nDeallocating memory more than once can lead to a double-free vulnerability. This can be exploited to corrupt the allocator's internal data structures, which can lead to denial-of-service attacks by crashing the program, or security vulnerabilities, by allowing an attacker to overwrite arbitrary memory locations.\n\n\n## Recommendation\nEnsure that all execution paths deallocate the allocated memory at most once. If possible, reassign the pointer to a null value after deallocating it. This will prevent double-free vulnerabilities since most deallocation functions will perform a null-pointer check before attempting to deallocate the memory.\n\n\n## Example\n\n```cpp\nint* f() {\n\tint *buff = malloc(SIZE*sizeof(int));\n\tdo_stuff(buff);\n\tfree(buff);\n\tint *new_buffer = malloc(SIZE*sizeof(int));\n\tfree(buff); // BAD: If new_buffer is assigned the same address as buff,\n // the memory allocator will free the new buffer memory region,\n // leading to use-after-free problems and memory corruption.\n\treturn new_buffer;\n}\n\n```\n\n## References\n* OWASP: [Doubly freeing memory](https://owasp.org/www-community/vulnerabilities/Doubly_freeing_memory).\n* Common Weakness Enumeration: [CWE-415](https://cwe.mitre.org/data/definitions/415.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "security", "external/cwe/cwe-415" ], + "description" : "Freeing a resource more than once can lead to undefined behavior and cause memory corruption.", + "id" : "cpp/double-free", + "kind" : "path-problem", + "name" : "Potential double free", + "precision" : "high", + "problem.severity" : "warning", + "security-severity" : "9.3" + } + }, { + "id" : "cpp/use-after-free", + "name" : "cpp/use-after-free", + "shortDescription" : { + "text" : "Potential use after free" + }, + "fullDescription" : { + "text" : "An allocated memory block is used after it has been freed. Behavior in such cases is undefined and can cause memory corruption." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Potential use after free\nThis rule finds accesses through a pointer of a memory location that has already been freed (i.e. through a dangling pointer). Such memory blocks have already been released to the dynamic memory manager, and modifying them can lead to anything from a segfault to memory corruption that would cause subsequent calls to the dynamic memory manger to behave erratically, to a possible security vulnerability.\n\n> WARNING: This check is an approximation, so some results may not be actual defects in the program. It is not possible in general to compute the values of pointers without running the program with all input data.\n\n## Recommendation\nEnsure that all execution paths that access memory through a pointer never access that pointer after it is freed.\n\n\n## Example\n\n```cpp\nvoid f() {\n\tchar* buf = new char[SIZE];\n\t...\n\tif (error) {\n\t\tdelete buf; //error handling has freed the buffer\n\t}\n\t...\n\tlog_contents(buf); //but it is still used here for logging\n\t...\n}\n\n```\n\n## References\n* I. Gerg. *An Overview and Example of the Buffer-Overflow Exploit*. IANewsletter vol 7 no 4. 2005.\n* M. Donaldson. *Inside the Buffer Overflow Attack: Mechanism, Method & Prevention*. SANS Institute InfoSec Reading Room. 2002.\n* Common Weakness Enumeration: [CWE-416](https://cwe.mitre.org/data/definitions/416.html).\n", + "markdown" : "# Potential use after free\nThis rule finds accesses through a pointer of a memory location that has already been freed (i.e. through a dangling pointer). Such memory blocks have already been released to the dynamic memory manager, and modifying them can lead to anything from a segfault to memory corruption that would cause subsequent calls to the dynamic memory manger to behave erratically, to a possible security vulnerability.\n\n> WARNING: This check is an approximation, so some results may not be actual defects in the program. It is not possible in general to compute the values of pointers without running the program with all input data.\n\n## Recommendation\nEnsure that all execution paths that access memory through a pointer never access that pointer after it is freed.\n\n\n## Example\n\n```cpp\nvoid f() {\n\tchar* buf = new char[SIZE];\n\t...\n\tif (error) {\n\t\tdelete buf; //error handling has freed the buffer\n\t}\n\t...\n\tlog_contents(buf); //but it is still used here for logging\n\t...\n}\n\n```\n\n## References\n* I. Gerg. *An Overview and Example of the Buffer-Overflow Exploit*. IANewsletter vol 7 no 4. 2005.\n* M. Donaldson. *Inside the Buffer Overflow Attack: Mechanism, Method & Prevention*. SANS Institute InfoSec Reading Room. 2002.\n* Common Weakness Enumeration: [CWE-416](https://cwe.mitre.org/data/definitions/416.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "security", "external/cwe/cwe-416" ], + "description" : "An allocated memory block is used after it has been freed. Behavior in such cases is undefined and can cause memory corruption.", + "id" : "cpp/use-after-free", + "kind" : "path-problem", + "name" : "Potential use after free", + "precision" : "high", + "problem.severity" : "warning", + "security-severity" : "9.3" + } + }, { + "id" : "cpp/static-buffer-overflow", + "name" : "cpp/static-buffer-overflow", + "shortDescription" : { + "text" : "Static array access may cause overflow" + }, + "fullDescription" : { + "text" : "Exceeding the size of a static array during write or access operations may result in a buffer overflow." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Static array access may cause overflow\nWhen you use static arrays you must ensure that you do not exceed the size of the array during write and access operations. If an operation attempts to write to or access an element that is outside the range of the array then this results in a buffer overflow. Buffer overflows can lead to anything from a segmentation fault to a security vulnerability.\n\n\n## Recommendation\nCheck the offsets and sizes used in the highlighted operations to ensure that a buffer overflow will not occur.\n\n\n## Example\n\n```cpp\n#define SIZE 30\n\nint f(char * s) {\n\tchar buf[20]; //buf not set to use SIZE macro\n\n\tstrncpy(buf, s, SIZE); //wrong: copy may exceed size of buf\n\n\tfor (int i = 0; i < SIZE; i++) { //wrong: upper limit that is higher than array size\n\t\tcout << array[i];\n\t}\n}\n\n```\n\n## References\n* I. Gerg. *An Overview and Example of the Buffer-Overflow Exploit*. IANewsletter vol 7 no 4. 2005.\n* M. Donaldson. *Inside the Buffer Overflow Attack: Mechanism, Method & Prevention*. SANS Institute InfoSec Reading Room. 2002.\n* Common Weakness Enumeration: [CWE-119](https://cwe.mitre.org/data/definitions/119.html).\n* Common Weakness Enumeration: [CWE-131](https://cwe.mitre.org/data/definitions/131.html).\n", + "markdown" : "# Static array access may cause overflow\nWhen you use static arrays you must ensure that you do not exceed the size of the array during write and access operations. If an operation attempts to write to or access an element that is outside the range of the array then this results in a buffer overflow. Buffer overflows can lead to anything from a segmentation fault to a security vulnerability.\n\n\n## Recommendation\nCheck the offsets and sizes used in the highlighted operations to ensure that a buffer overflow will not occur.\n\n\n## Example\n\n```cpp\n#define SIZE 30\n\nint f(char * s) {\n\tchar buf[20]; //buf not set to use SIZE macro\n\n\tstrncpy(buf, s, SIZE); //wrong: copy may exceed size of buf\n\n\tfor (int i = 0; i < SIZE; i++) { //wrong: upper limit that is higher than array size\n\t\tcout << array[i];\n\t}\n}\n\n```\n\n## References\n* I. Gerg. *An Overview and Example of the Buffer-Overflow Exploit*. IANewsletter vol 7 no 4. 2005.\n* M. Donaldson. *Inside the Buffer Overflow Attack: Mechanism, Method & Prevention*. SANS Institute InfoSec Reading Room. 2002.\n* Common Weakness Enumeration: [CWE-119](https://cwe.mitre.org/data/definitions/119.html).\n* Common Weakness Enumeration: [CWE-131](https://cwe.mitre.org/data/definitions/131.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "security", "external/cwe/cwe-119", "external/cwe/cwe-131" ], + "description" : "Exceeding the size of a static array during write or access operations\n may result in a buffer overflow.", + "id" : "cpp/static-buffer-overflow", + "kind" : "problem", + "name" : "Static array access may cause overflow", + "precision" : "high", + "problem.severity" : "warning", + "security-severity" : "9.3" + } + }, { + "id" : "cpp/unsafe-strncat", + "name" : "cpp/unsafe-strncat", + "shortDescription" : { + "text" : "Potentially unsafe call to strncat" + }, + "fullDescription" : { + "text" : "Calling 'strncat' with an incorrect size argument may result in a buffer overflow." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Potentially unsafe call to strncat\nThe standard library function `strncat` appends a source string to a target string. The third argument defines the maximum number of characters to append and should be less than or equal to the remaining space in the destination buffer.\n\nCalls of the form `strncat(dest, src, strlen(dest))` or `strncat(dest, src, sizeof(dest))` set the third argument to the entire size of the destination buffer. Executing a call of this type may cause a buffer overflow unless the buffer is known to be empty.\n\nSimilarly, calls of the form `strncat(dest, src, sizeof (dest) - strlen (dest))` allow one byte to be written outside the `dest` buffer.\n\nBuffer overflows can lead to anything from a segmentation fault to a security vulnerability.\n\n\n## Recommendation\nCheck the highlighted function calls carefully to ensure that no buffer overflow is possible. For a more robust solution, consider updating the function call to include the remaining space in the destination buffer.\n\n\n## Example\n\n```cpp\nstrncat(dest, src, strlen(dest)); //wrong: should use remaining size of dest\n\nstrncat(dest, src, sizeof(dest)); //wrong: should use remaining size of dest. \n //Also fails if dest is a pointer and not an array.\n \nstrncat(dest, source, sizeof(dest) - strlen(dest)); // wrong: writes a zero byte past the `dest` buffer.\n\nstrncat(dest, source, sizeof(dest) - strlen(dest) - 1); // correct: reserves space for the zero byte.\n\n```\n\n## References\n* cplusplus.com: [strncat](http://www.cplusplus.com/reference/clibrary/cstring/strncat/), [strncpy](http://www.cplusplus.com/reference/clibrary/cstring/strncpy/).\n* I. Gerg, *An Overview and Example of the Buffer-Overflow Exploit*. IANewsletter vol 7 no 4, 2005.\n* M. Donaldson, *Inside the Buffer Overflow Attack: Mechanism, Method & Prevention*. SANS Institute InfoSec Reading Room, 2002.\n* CERT C Coding Standard: [STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator](https://wiki.sei.cmu.edu/confluence/display/c/STR31-C.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator).\n* Common Weakness Enumeration: [CWE-788](https://cwe.mitre.org/data/definitions/788.html).\n* Common Weakness Enumeration: [CWE-676](https://cwe.mitre.org/data/definitions/676.html).\n* Common Weakness Enumeration: [CWE-119](https://cwe.mitre.org/data/definitions/119.html).\n* Common Weakness Enumeration: [CWE-251](https://cwe.mitre.org/data/definitions/251.html).\n", + "markdown" : "# Potentially unsafe call to strncat\nThe standard library function `strncat` appends a source string to a target string. The third argument defines the maximum number of characters to append and should be less than or equal to the remaining space in the destination buffer.\n\nCalls of the form `strncat(dest, src, strlen(dest))` or `strncat(dest, src, sizeof(dest))` set the third argument to the entire size of the destination buffer. Executing a call of this type may cause a buffer overflow unless the buffer is known to be empty.\n\nSimilarly, calls of the form `strncat(dest, src, sizeof (dest) - strlen (dest))` allow one byte to be written outside the `dest` buffer.\n\nBuffer overflows can lead to anything from a segmentation fault to a security vulnerability.\n\n\n## Recommendation\nCheck the highlighted function calls carefully to ensure that no buffer overflow is possible. For a more robust solution, consider updating the function call to include the remaining space in the destination buffer.\n\n\n## Example\n\n```cpp\nstrncat(dest, src, strlen(dest)); //wrong: should use remaining size of dest\n\nstrncat(dest, src, sizeof(dest)); //wrong: should use remaining size of dest. \n //Also fails if dest is a pointer and not an array.\n \nstrncat(dest, source, sizeof(dest) - strlen(dest)); // wrong: writes a zero byte past the `dest` buffer.\n\nstrncat(dest, source, sizeof(dest) - strlen(dest) - 1); // correct: reserves space for the zero byte.\n\n```\n\n## References\n* cplusplus.com: [strncat](http://www.cplusplus.com/reference/clibrary/cstring/strncat/), [strncpy](http://www.cplusplus.com/reference/clibrary/cstring/strncpy/).\n* I. Gerg, *An Overview and Example of the Buffer-Overflow Exploit*. IANewsletter vol 7 no 4, 2005.\n* M. Donaldson, *Inside the Buffer Overflow Attack: Mechanism, Method & Prevention*. SANS Institute InfoSec Reading Room, 2002.\n* CERT C Coding Standard: [STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator](https://wiki.sei.cmu.edu/confluence/display/c/STR31-C.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator).\n* Common Weakness Enumeration: [CWE-788](https://cwe.mitre.org/data/definitions/788.html).\n* Common Weakness Enumeration: [CWE-676](https://cwe.mitre.org/data/definitions/676.html).\n* Common Weakness Enumeration: [CWE-119](https://cwe.mitre.org/data/definitions/119.html).\n* Common Weakness Enumeration: [CWE-251](https://cwe.mitre.org/data/definitions/251.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "correctness", "security", "external/cwe/cwe-788", "external/cwe/cwe-676", "external/cwe/cwe-119", "external/cwe/cwe-251" ], + "description" : "Calling 'strncat' with an incorrect size argument may result in a buffer overflow.", + "id" : "cpp/unsafe-strncat", + "kind" : "problem", + "name" : "Potentially unsafe call to strncat", + "precision" : "medium", + "problem.severity" : "warning", + "security-severity" : "9.3" + } + }, { + "id" : "cpp/bad-strncpy-size", + "name" : "cpp/bad-strncpy-size", + "shortDescription" : { + "text" : "Possibly wrong buffer size in string copy" + }, + "fullDescription" : { + "text" : "Calling 'strncpy' with the size of the source buffer as the third argument may result in a buffer overflow." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Possibly wrong buffer size in string copy\nThe standard library function `strncpy` copies a source string to a destination buffer. The third argument defines the maximum number of characters to copy and should be less than or equal to the size of the destination buffer. Calls of the form `strncpy(dest, src, strlen(src))` or `strncpy(dest, src, sizeof(src))` incorrectly set the third argument to the size of the source buffer. Executing a call of this type may cause a buffer overflow. Buffer overflows can lead to anything from a segmentation fault to a security vulnerability.\n\n\n## Recommendation\nCheck the highlighted function calls carefully, and ensure that the size parameter is derived from the size of the destination buffer, not the source buffer.\n\n\n## Example\n\n```cpp\nstrncpy(dest, src, sizeof(src)); //wrong: size of dest should be used\nstrncpy(dest, src, strlen(src)); //wrong: size of dest should be used\n\n```\n\n## References\n* cplusplus.com: [strncpy](http://www.cplusplus.com/reference/clibrary/cstring/strncpy/).\n* I. Gerg. *An Overview and Example of the Buffer-Overflow Exploit*. IANewsletter vol 7 no 4. 2005.\n* M. Donaldson. *Inside the Buffer Overflow Attack: Mechanism, Method & Prevention*. SANS Institute InfoSec Reading Room. 2002.\n* Common Weakness Enumeration: [CWE-676](https://cwe.mitre.org/data/definitions/676.html).\n* Common Weakness Enumeration: [CWE-119](https://cwe.mitre.org/data/definitions/119.html).\n* Common Weakness Enumeration: [CWE-251](https://cwe.mitre.org/data/definitions/251.html).\n", + "markdown" : "# Possibly wrong buffer size in string copy\nThe standard library function `strncpy` copies a source string to a destination buffer. The third argument defines the maximum number of characters to copy and should be less than or equal to the size of the destination buffer. Calls of the form `strncpy(dest, src, strlen(src))` or `strncpy(dest, src, sizeof(src))` incorrectly set the third argument to the size of the source buffer. Executing a call of this type may cause a buffer overflow. Buffer overflows can lead to anything from a segmentation fault to a security vulnerability.\n\n\n## Recommendation\nCheck the highlighted function calls carefully, and ensure that the size parameter is derived from the size of the destination buffer, not the source buffer.\n\n\n## Example\n\n```cpp\nstrncpy(dest, src, sizeof(src)); //wrong: size of dest should be used\nstrncpy(dest, src, strlen(src)); //wrong: size of dest should be used\n\n```\n\n## References\n* cplusplus.com: [strncpy](http://www.cplusplus.com/reference/clibrary/cstring/strncpy/).\n* I. Gerg. *An Overview and Example of the Buffer-Overflow Exploit*. IANewsletter vol 7 no 4. 2005.\n* M. Donaldson. *Inside the Buffer Overflow Attack: Mechanism, Method & Prevention*. SANS Institute InfoSec Reading Room. 2002.\n* Common Weakness Enumeration: [CWE-676](https://cwe.mitre.org/data/definitions/676.html).\n* Common Weakness Enumeration: [CWE-119](https://cwe.mitre.org/data/definitions/119.html).\n* Common Weakness Enumeration: [CWE-251](https://cwe.mitre.org/data/definitions/251.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "correctness", "security", "external/cwe/cwe-676", "external/cwe/cwe-119", "external/cwe/cwe-251" ], + "description" : "Calling 'strncpy' with the size of the source buffer\n as the third argument may result in a buffer overflow.", + "id" : "cpp/bad-strncpy-size", + "kind" : "problem", + "name" : "Possibly wrong buffer size in string copy", + "precision" : "medium", + "problem.severity" : "warning", + "security-severity" : "9.3" + } + }, { + "id" : "cpp/unsafe-strcat", + "name" : "cpp/unsafe-strcat", + "shortDescription" : { + "text" : "Potentially unsafe use of strcat" + }, + "fullDescription" : { + "text" : "Using 'strcat' without checking the size of the source string may result in a buffer overflow" + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Potentially unsafe use of strcat\nThe standard library function `strcat` appends a source string to a target string. If you do not check the size of the source string then you cannot guarantee that appending the data to the target string will not cause a buffer overflow. Buffer overflows can lead to anything from a segmentation fault to a security vulnerability.\n\n\n## Recommendation\nCheck the highlighted function calls carefully to ensure that no buffer overflow is possible. For a more robust solution, consider adding explicit range checks or using the `strncat` function instead.\n\n\n## Example\n\n```cpp\nvoid f(char *s) {\n\tchar buf[80];\n\tstrcpy(buf, \"s: \");\n\tstrcat(buf, s); // wrong: buffer not checked before strcat\n}\n\nvoid g(char *s) {\n\tchar buf[80];\n\tstrcpy(buf, \"s: \");\n\tif(strlen(s) < 77)\n\t\tstrcat(buf, s); // correct: buffer size checked before strcat\n}\n\n```\n\n## References\n* I. Gerg, *An Overview and Example of the Buffer-Overflow Exploit*. IANewsletter vol 7, no 4, 2005.\n* M. Donaldson, *Inside the Buffer Overflow Attack: Mechanism, Method & Prevention*. SANS Institute InfoSec Reading Room. 2002.\n* Common Weakness Enumeration: [CWE-676](https://cwe.mitre.org/data/definitions/676.html).\n* Common Weakness Enumeration: [CWE-120](https://cwe.mitre.org/data/definitions/120.html).\n* Common Weakness Enumeration: [CWE-251](https://cwe.mitre.org/data/definitions/251.html).\n", + "markdown" : "# Potentially unsafe use of strcat\nThe standard library function `strcat` appends a source string to a target string. If you do not check the size of the source string then you cannot guarantee that appending the data to the target string will not cause a buffer overflow. Buffer overflows can lead to anything from a segmentation fault to a security vulnerability.\n\n\n## Recommendation\nCheck the highlighted function calls carefully to ensure that no buffer overflow is possible. For a more robust solution, consider adding explicit range checks or using the `strncat` function instead.\n\n\n## Example\n\n```cpp\nvoid f(char *s) {\n\tchar buf[80];\n\tstrcpy(buf, \"s: \");\n\tstrcat(buf, s); // wrong: buffer not checked before strcat\n}\n\nvoid g(char *s) {\n\tchar buf[80];\n\tstrcpy(buf, \"s: \");\n\tif(strlen(s) < 77)\n\t\tstrcat(buf, s); // correct: buffer size checked before strcat\n}\n\n```\n\n## References\n* I. Gerg, *An Overview and Example of the Buffer-Overflow Exploit*. IANewsletter vol 7, no 4, 2005.\n* M. Donaldson, *Inside the Buffer Overflow Attack: Mechanism, Method & Prevention*. SANS Institute InfoSec Reading Room. 2002.\n* Common Weakness Enumeration: [CWE-676](https://cwe.mitre.org/data/definitions/676.html).\n* Common Weakness Enumeration: [CWE-120](https://cwe.mitre.org/data/definitions/120.html).\n* Common Weakness Enumeration: [CWE-251](https://cwe.mitre.org/data/definitions/251.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "correctness", "security", "external/cwe/cwe-676", "external/cwe/cwe-120", "external/cwe/cwe-251" ], + "description" : "Using 'strcat' without checking the size of the source string\n may result in a buffer overflow", + "id" : "cpp/unsafe-strcat", + "kind" : "problem", + "name" : "Potentially unsafe use of strcat", + "precision" : "medium", + "problem.severity" : "warning", + "security-severity" : "9.8" + } + }, { + "id" : "cpp/uninitialized-local", + "name" : "cpp/uninitialized-local", + "shortDescription" : { + "text" : "Potentially uninitialized local variable" + }, + "fullDescription" : { + "text" : "Reading from a local variable that has not been assigned to will typically yield garbage." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Potentially uninitialized local variable\nA local non-static variable of a non-class type has an undefined value before it is initialized. For example, it is incorrect to rely on an uninitialized integer to have the value `0`.\n\n\n## Recommendation\nReview the code and consider whether the variable should have an initializer or whether some path through the program lacks an assignment to the variable.\n\n\n## Example\nThe function `absWrong` does not initialize the variable `j` in the case where `i = 0`. Functions `absCorrect1` and `absCorrect2` remedy this deficiency by adding an initializer and adding an assignment to one of the paths through the program, respectively.\n\n\n```cpp\nint absWrong(int i) {\n\tint j;\n\tif (i > 0) {\n\t\tj = i;\n\t} else if (i < 0) {\n\t\tj = -i;\n\t}\n\treturn j; // wrong: j may not be initialized before use\n}\n\nint absCorrect1(int i) {\n\tint j = 0;\n\tif (i > 0) {\n\t\tj = i;\n\t} else if (i < 0) {\n\t\tj = -i;\n\t}\n\treturn j; // correct: j always initialized before use\n}\n\nint absCorrect2(int i) {\n\tint j;\n\tif (i > 0) {\n\t\tj = i;\n\t} else if (i < 0) {\n\t\tj = -i;\n\t} else {\n\t\tj = 0;\n\t}\n\treturn j; // correct: j always initialized before use\n}\n```\n\n## References\n* ISO/IEC 9899:2011: [Programming languages - C (Section 6.3.2.1)](https://www.iso.org/standard/57853.html).\n* Common Weakness Enumeration: [CWE-665](https://cwe.mitre.org/data/definitions/665.html).\n* Common Weakness Enumeration: [CWE-457](https://cwe.mitre.org/data/definitions/457.html).\n", + "markdown" : "# Potentially uninitialized local variable\nA local non-static variable of a non-class type has an undefined value before it is initialized. For example, it is incorrect to rely on an uninitialized integer to have the value `0`.\n\n\n## Recommendation\nReview the code and consider whether the variable should have an initializer or whether some path through the program lacks an assignment to the variable.\n\n\n## Example\nThe function `absWrong` does not initialize the variable `j` in the case where `i = 0`. Functions `absCorrect1` and `absCorrect2` remedy this deficiency by adding an initializer and adding an assignment to one of the paths through the program, respectively.\n\n\n```cpp\nint absWrong(int i) {\n\tint j;\n\tif (i > 0) {\n\t\tj = i;\n\t} else if (i < 0) {\n\t\tj = -i;\n\t}\n\treturn j; // wrong: j may not be initialized before use\n}\n\nint absCorrect1(int i) {\n\tint j = 0;\n\tif (i > 0) {\n\t\tj = i;\n\t} else if (i < 0) {\n\t\tj = -i;\n\t}\n\treturn j; // correct: j always initialized before use\n}\n\nint absCorrect2(int i) {\n\tint j;\n\tif (i > 0) {\n\t\tj = i;\n\t} else if (i < 0) {\n\t\tj = -i;\n\t} else {\n\t\tj = 0;\n\t}\n\treturn j; // correct: j always initialized before use\n}\n```\n\n## References\n* ISO/IEC 9899:2011: [Programming languages - C (Section 6.3.2.1)](https://www.iso.org/standard/57853.html).\n* Common Weakness Enumeration: [CWE-665](https://cwe.mitre.org/data/definitions/665.html).\n* Common Weakness Enumeration: [CWE-457](https://cwe.mitre.org/data/definitions/457.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-665", "external/cwe/cwe-457" ], + "description" : "Reading from a local variable that has not been assigned to\n will typically yield garbage.", + "id" : "cpp/uninitialized-local", + "kind" : "problem", + "name" : "Potentially uninitialized local variable", + "precision" : "medium", + "problem.severity" : "warning", + "security-severity" : "7.8" + } + }, { + "id" : "cpp/suspicious-sizeof", + "name" : "cpp/suspicious-sizeof", + "shortDescription" : { + "text" : "Suspicious 'sizeof' use" + }, + "fullDescription" : { + "text" : "Taking 'sizeof' of an array parameter is often mistakenly thought to yield the size of the underlying array, but it always yields the machine pointer size." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Suspicious 'sizeof' use\nThis rule finds expressions that take the size of a function parameter of array type. In C, function parameters of array type are treated as if they had the corresponding pointer type, so their size is always the size of the pointer type (typically either four or eight). In particular, one cannot determine the size of a memory buffer passed as a parameter in this way. Using the `sizeof` operator on pointer types will produce unexpected results if the developer intended to get the size of an array instead of the pointer.\n\n\n## Recommendation\nModify the function to take an extra argument indicating the buffer size.\n\n\n## Example\n\n```cpp\nvoid f(char s[]) {\n\tint size = sizeof(s); //wrong: s is now a char*, not an array. \n\t //sizeof(s) will evaluate to sizeof(char *)\n}\n\n```\n\n## References\n* Comp.lang.c, Frequently Asked Questions: [Question 6.3: So what is meant by the \"equivalence of pointers and arrays\" in C?](http://c-faq.com/aryptr/aryptrequiv.html).\n* Common Weakness Enumeration: [CWE-467](https://cwe.mitre.org/data/definitions/467.html).\n", + "markdown" : "# Suspicious 'sizeof' use\nThis rule finds expressions that take the size of a function parameter of array type. In C, function parameters of array type are treated as if they had the corresponding pointer type, so their size is always the size of the pointer type (typically either four or eight). In particular, one cannot determine the size of a memory buffer passed as a parameter in this way. Using the `sizeof` operator on pointer types will produce unexpected results if the developer intended to get the size of an array instead of the pointer.\n\n\n## Recommendation\nModify the function to take an extra argument indicating the buffer size.\n\n\n## Example\n\n```cpp\nvoid f(char s[]) {\n\tint size = sizeof(s); //wrong: s is now a char*, not an array. \n\t //sizeof(s) will evaluate to sizeof(char *)\n}\n\n```\n\n## References\n* Comp.lang.c, Frequently Asked Questions: [Question 6.3: So what is meant by the \"equivalence of pointers and arrays\" in C?](http://c-faq.com/aryptr/aryptrequiv.html).\n* Common Weakness Enumeration: [CWE-467](https://cwe.mitre.org/data/definitions/467.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "correctness", "security", "external/cwe/cwe-467" ], + "description" : "Taking 'sizeof' of an array parameter is often mistakenly thought\n to yield the size of the underlying array, but it always yields\n the machine pointer size.", + "id" : "cpp/suspicious-sizeof", + "kind" : "problem", + "name" : "Suspicious 'sizeof' use", + "precision" : "medium", + "problem.severity" : "warning", + "security-severity" : "8.8" + } + }, { + "id" : "cpp/incorrect-not-operator-usage", + "name" : "cpp/incorrect-not-operator-usage", + "shortDescription" : { + "text" : "Incorrect 'not' operator usage" + }, + "fullDescription" : { + "text" : "Usage of a logical-not (!) operator as an operand for a bit-wise operation. This commonly indicates the usage of an incorrect operator instead of the bit-wise not (~) operator, also known as ones' complement operator." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Incorrect 'not' operator usage\nThis rule finds logical-not operator usage as an operator for in a bit-wise operation.\n\nDue to the nature of logical operation result value, only the lowest bit could possibly be set, and it is unlikely to be intent in bitwise operations. Violations are often indicative of a typo, using a logical-not (`!`) operator instead of the bit-wise not (`~`) operator.\n\nThis rule is restricted to analyze bit-wise and (`&`) and bit-wise or (`|`) operation in order to provide better precision.\n\nThis rule ignores instances where a double negation (`!!`) is explicitly used as the operator of the bitwise operation, as this is a commonly used as a mechanism to normalize an integer value to either 1 or 0.\n\nNOTE: It is not recommended to use this rule in kernel code or older C code as it will likely find several false positive instances.\n\n\n## Recommendation\nCarefully inspect the flagged expressions. Consider the intent in the code logic, and decide whether it is necessary to change the not operator.\n\n\n## Example\n\n```cpp\n#define FLAGS 0x4004\n\nvoid f_warning(int i)\n{\n // The usage of the logical not operator in this case is unlikely to be correct\n // as the output is being used as an operator for a bit-wise and operation\n if (i & !FLAGS) \n {\n // code\n }\n}\n\n\nvoid f_fixed(int i)\n{\n if (i & ~FLAGS) // Changing the logical not operator for the bit-wise not operator would fix this logic\n {\n // code\n }\n}\n```\n\n## References\n* [warning C6317: incorrect operator: logical-not (!) is not interchangeable with ones-complement (~)](https://docs.microsoft.com/en-us/visualstudio/code-quality/c6317?view=vs-2017)\n* Common Weakness Enumeration: [CWE-480](https://cwe.mitre.org/data/definitions/480.html).\n", + "markdown" : "# Incorrect 'not' operator usage\nThis rule finds logical-not operator usage as an operator for in a bit-wise operation.\n\nDue to the nature of logical operation result value, only the lowest bit could possibly be set, and it is unlikely to be intent in bitwise operations. Violations are often indicative of a typo, using a logical-not (`!`) operator instead of the bit-wise not (`~`) operator.\n\nThis rule is restricted to analyze bit-wise and (`&`) and bit-wise or (`|`) operation in order to provide better precision.\n\nThis rule ignores instances where a double negation (`!!`) is explicitly used as the operator of the bitwise operation, as this is a commonly used as a mechanism to normalize an integer value to either 1 or 0.\n\nNOTE: It is not recommended to use this rule in kernel code or older C code as it will likely find several false positive instances.\n\n\n## Recommendation\nCarefully inspect the flagged expressions. Consider the intent in the code logic, and decide whether it is necessary to change the not operator.\n\n\n## Example\n\n```cpp\n#define FLAGS 0x4004\n\nvoid f_warning(int i)\n{\n // The usage of the logical not operator in this case is unlikely to be correct\n // as the output is being used as an operator for a bit-wise and operation\n if (i & !FLAGS) \n {\n // code\n }\n}\n\n\nvoid f_fixed(int i)\n{\n if (i & ~FLAGS) // Changing the logical not operator for the bit-wise not operator would fix this logic\n {\n // code\n }\n}\n```\n\n## References\n* [warning C6317: incorrect operator: logical-not (!) is not interchangeable with ones-complement (~)](https://docs.microsoft.com/en-us/visualstudio/code-quality/c6317?view=vs-2017)\n* Common Weakness Enumeration: [CWE-480](https://cwe.mitre.org/data/definitions/480.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-480" ], + "description" : "Usage of a logical-not (!) operator as an operand for a bit-wise operation.\n This commonly indicates the usage of an incorrect operator instead of the bit-wise not (~) operator,\n also known as ones' complement operator.", + "id" : "cpp/incorrect-not-operator-usage", + "kind" : "problem", + "name" : "Incorrect 'not' operator usage", + "precision" : "medium", + "problem.severity" : "warning", + "security-severity" : "7.5" + } + }, { + "id" : "cpp/offset-use-before-range-check", + "name" : "cpp/offset-use-before-range-check", + "shortDescription" : { + "text" : "Array offset used before range check" + }, + "fullDescription" : { + "text" : "Accessing an array offset before checking the range means that the program may attempt to read beyond the end of a buffer" + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Array offset used before range check\nThe program contains an and-expression where the array access is defined before the range check. Consequently the array is accessed without any bounds checking. The range check does not protect the program from segmentation faults caused by attempts to read beyond the end of a buffer.\n\n\n## Recommendation\nUpdate the and-expression so that the range check precedes the array offset. This will ensure that the bounds are checked before the array is accessed.\n\n\n## Example\nThe `find` function can read past the end of the buffer pointed to by `str` if `start` is longer than or equal to the length of the buffer (or longer than `len`, depending on the contents of the buffer).\n\n\n```c\nint find(int start, char *str, char goal)\n{\n int len = strlen(str);\n //Potential buffer overflow\n for (int i = start; str[i] != 0 && i < len; i++) { \n if (str[i] == goal)\n return i; \n }\n return -1;\n}\n\nint findRangeCheck(int start, char *str, char goal)\n{\n int len = strlen(str);\n //Range check protects against buffer overflow\n for (int i = start; i < len && str[i] != 0 ; i++) {\n if (str[i] == goal)\n return i; \n }\n return -1;\n}\n\n\n\n\n```\nUpdate the and-expression so that the range check precedes the array offset (for example, the `findRangeCheck` function).\n\n\n## References\n* cplusplus.com: [ C++: array](http://www.cplusplus.com/reference/array/array/).\n* Wikipedia: [ Bounds checking](http://en.wikipedia.org/wiki/Bounds_checking).\n* Common Weakness Enumeration: [CWE-120](https://cwe.mitre.org/data/definitions/120.html).\n* Common Weakness Enumeration: [CWE-125](https://cwe.mitre.org/data/definitions/125.html).\n", + "markdown" : "# Array offset used before range check\nThe program contains an and-expression where the array access is defined before the range check. Consequently the array is accessed without any bounds checking. The range check does not protect the program from segmentation faults caused by attempts to read beyond the end of a buffer.\n\n\n## Recommendation\nUpdate the and-expression so that the range check precedes the array offset. This will ensure that the bounds are checked before the array is accessed.\n\n\n## Example\nThe `find` function can read past the end of the buffer pointed to by `str` if `start` is longer than or equal to the length of the buffer (or longer than `len`, depending on the contents of the buffer).\n\n\n```c\nint find(int start, char *str, char goal)\n{\n int len = strlen(str);\n //Potential buffer overflow\n for (int i = start; str[i] != 0 && i < len; i++) { \n if (str[i] == goal)\n return i; \n }\n return -1;\n}\n\nint findRangeCheck(int start, char *str, char goal)\n{\n int len = strlen(str);\n //Range check protects against buffer overflow\n for (int i = start; i < len && str[i] != 0 ; i++) {\n if (str[i] == goal)\n return i; \n }\n return -1;\n}\n\n\n\n\n```\nUpdate the and-expression so that the range check precedes the array offset (for example, the `findRangeCheck` function).\n\n\n## References\n* cplusplus.com: [ C++: array](http://www.cplusplus.com/reference/array/array/).\n* Wikipedia: [ Bounds checking](http://en.wikipedia.org/wiki/Bounds_checking).\n* Common Weakness Enumeration: [CWE-120](https://cwe.mitre.org/data/definitions/120.html).\n* Common Weakness Enumeration: [CWE-125](https://cwe.mitre.org/data/definitions/125.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "security", "external/cwe/cwe-120", "external/cwe/cwe-125" ], + "description" : "Accessing an array offset before checking the range means that\n the program may attempt to read beyond the end of a buffer", + "id" : "cpp/offset-use-before-range-check", + "kind" : "problem", + "name" : "Array offset used before range check", + "precision" : "medium", + "problem.severity" : "warning", + "security-severity" : "8.2" + } + }, { + "id" : "cpp/comma-before-misleading-indentation", + "name" : "cpp/comma-before-misleading-indentation", + "shortDescription" : { + "text" : "Comma before misleading indentation" + }, + "fullDescription" : { + "text" : "If expressions before and after a comma operator use different indentation, it is easy to misread the purpose of the code." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Comma before misleading indentation\nIf the expression after the comma operator starts at an earlier column than the expression before the comma, then this suspicious indentation possibly indicates a logic error, caused by a typo that may escape visual inspection.\n\n> WARNING: This query has medium precision because CodeQL currently does not distinguish between tabs and spaces in whitespace. If a file contains mixed tabs and spaces, alerts may highlight code that is correctly indented for one value of tab size but not for other tab sizes.\n\n## Recommendation\nTo ensure that your code is easy to read and review, use standard indentation around the comma operator. Always begin the right-hand-side operand at the same level of indentation (column number) as the left-hand-side operand. This makes it easier for other developers to see the intended behavior of your code.\n\nUse whitespace consistently to communicate your coding intentions. Where possible, avoid mixing tabs and spaces within a file. If you need to mix them, use them consistently.\n\n\n## Example\nThis example shows three different ways of writing the same code. The first example contains a comma instead of a semicolon which means that the final line is part of the `if` statement, even though the indentation suggests that it is intended to be separate. The second example looks different but is functionally the same as the first example. It is more likely that the developer intended to write the third example.\n\n\n```cpp\n/*\n * In this example, the developer intended to use a semicolon but accidentally used a comma:\n */\n\nenum privileges entitlements = NONE;\n\nif (is_admin)\n entitlements = FULL, // BAD\n\nrestrict_privileges(entitlements);\n\n/*\n * The use of a comma means that the first example is equivalent to this second example:\n */\n\nenum privileges entitlements = NONE;\n\nif (is_admin) {\n entitlements = FULL;\n restrict_privileges(entitlements);\n}\n\n/*\n * The indentation of the first example suggests that the developer probably intended the following code:\n */\n\nenum privileges entitlements = NONE;\n\nif (is_admin)\n entitlements = FULL; // GOOD\n\nrestrict_privileges(entitlements);\n\n```\n\n## References\n* Wikipedia: [Comma operator](https://en.wikipedia.org/wiki/Comma_operator)\n* Wikipedia: [Indentation style — Tabs, spaces, and size of indentations](https://en.wikipedia.org/wiki/Indentation_style#Tabs,_spaces,_and_size_of_indentations)\n* Common Weakness Enumeration: [CWE-1078](https://cwe.mitre.org/data/definitions/1078.html).\n* Common Weakness Enumeration: [CWE-670](https://cwe.mitre.org/data/definitions/670.html).\n", + "markdown" : "# Comma before misleading indentation\nIf the expression after the comma operator starts at an earlier column than the expression before the comma, then this suspicious indentation possibly indicates a logic error, caused by a typo that may escape visual inspection.\n\n> WARNING: This query has medium precision because CodeQL currently does not distinguish between tabs and spaces in whitespace. If a file contains mixed tabs and spaces, alerts may highlight code that is correctly indented for one value of tab size but not for other tab sizes.\n\n## Recommendation\nTo ensure that your code is easy to read and review, use standard indentation around the comma operator. Always begin the right-hand-side operand at the same level of indentation (column number) as the left-hand-side operand. This makes it easier for other developers to see the intended behavior of your code.\n\nUse whitespace consistently to communicate your coding intentions. Where possible, avoid mixing tabs and spaces within a file. If you need to mix them, use them consistently.\n\n\n## Example\nThis example shows three different ways of writing the same code. The first example contains a comma instead of a semicolon which means that the final line is part of the `if` statement, even though the indentation suggests that it is intended to be separate. The second example looks different but is functionally the same as the first example. It is more likely that the developer intended to write the third example.\n\n\n```cpp\n/*\n * In this example, the developer intended to use a semicolon but accidentally used a comma:\n */\n\nenum privileges entitlements = NONE;\n\nif (is_admin)\n entitlements = FULL, // BAD\n\nrestrict_privileges(entitlements);\n\n/*\n * The use of a comma means that the first example is equivalent to this second example:\n */\n\nenum privileges entitlements = NONE;\n\nif (is_admin) {\n entitlements = FULL;\n restrict_privileges(entitlements);\n}\n\n/*\n * The indentation of the first example suggests that the developer probably intended the following code:\n */\n\nenum privileges entitlements = NONE;\n\nif (is_admin)\n entitlements = FULL; // GOOD\n\nrestrict_privileges(entitlements);\n\n```\n\n## References\n* Wikipedia: [Comma operator](https://en.wikipedia.org/wiki/Comma_operator)\n* Wikipedia: [Indentation style — Tabs, spaces, and size of indentations](https://en.wikipedia.org/wiki/Indentation_style#Tabs,_spaces,_and_size_of_indentations)\n* Common Weakness Enumeration: [CWE-1078](https://cwe.mitre.org/data/definitions/1078.html).\n* Common Weakness Enumeration: [CWE-670](https://cwe.mitre.org/data/definitions/670.html).\n" + }, + "properties" : { + "tags" : [ "maintainability", "readability", "security", "external/cwe/cwe-1078", "external/cwe/cwe-670" ], + "description" : "If expressions before and after a comma operator use different indentation, it is easy to misread the purpose of the code.", + "id" : "cpp/comma-before-misleading-indentation", + "kind" : "problem", + "name" : "Comma before misleading indentation", + "precision" : "medium", + "problem.severity" : "warning", + "security-severity" : "7.8" + } + }, { + "id" : "cpp/incorrect-allocation-error-handling", + "name" : "cpp/incorrect-allocation-error-handling", + "shortDescription" : { + "text" : "Incorrect allocation-error handling" + }, + "fullDescription" : { + "text" : "Mixing up the failure conditions of 'operator new' and 'operator new(std::nothrow)' can result in unexpected behavior." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Incorrect allocation-error handling\nDifferent overloads of the `new` operator handle allocation failures in different ways. If `new T` fails for some type `T`, it throws a `std::bad_alloc` exception, but `new(std::nothrow) T` returns a null pointer. If the programmer does not use the corresponding method of error handling, allocation failure may go unhandled and could cause the program to behave in unexpected ways.\n\n\n## Recommendation\nMake sure that exceptions are handled appropriately if `new T` is used. On the other hand, make sure to handle the possibility of null pointers if `new(std::nothrow) T` is used.\n\n\n## Example\n\n```cpp\n// BAD: the allocation will throw an unhandled exception\n// instead of returning a null pointer.\nvoid bad1(std::size_t length) noexcept {\n int* dest = new int[length];\n if(!dest) {\n return;\n }\n std::memset(dest, 0, length);\n // ...\n}\n\n// BAD: the allocation won't throw an exception, but\n// instead return a null pointer.\nvoid bad2(std::size_t length) noexcept {\n try {\n int* dest = new(std::nothrow) int[length];\n std::memset(dest, 0, length);\n // ...\n } catch(std::bad_alloc&) {\n // ...\n }\n}\n\n// GOOD: the allocation failure is handled appropriately.\nvoid good1(std::size_t length) noexcept {\n try {\n int* dest = new int[length];\n std::memset(dest, 0, length);\n // ...\n } catch(std::bad_alloc&) {\n // ...\n }\n}\n\n// GOOD: the allocation failure is handled appropriately.\nvoid good2(std::size_t length) noexcept {\n int* dest = new int[length];\n if(!dest) {\n return;\n }\n std::memset(dest, 0, length);\n // ...\n}\n\n```\n\n## References\n* CERT C++ Coding Standard: [MEM52-CPP. Detect and handle memory allocation errors](https://wiki.sei.cmu.edu/confluence/display/cplusplus/MEM52-CPP.+Detect+and+handle+memory+allocation+errors).\n* Common Weakness Enumeration: [CWE-570](https://cwe.mitre.org/data/definitions/570.html).\n* Common Weakness Enumeration: [CWE-252](https://cwe.mitre.org/data/definitions/252.html).\n* Common Weakness Enumeration: [CWE-755](https://cwe.mitre.org/data/definitions/755.html).\n", + "markdown" : "# Incorrect allocation-error handling\nDifferent overloads of the `new` operator handle allocation failures in different ways. If `new T` fails for some type `T`, it throws a `std::bad_alloc` exception, but `new(std::nothrow) T` returns a null pointer. If the programmer does not use the corresponding method of error handling, allocation failure may go unhandled and could cause the program to behave in unexpected ways.\n\n\n## Recommendation\nMake sure that exceptions are handled appropriately if `new T` is used. On the other hand, make sure to handle the possibility of null pointers if `new(std::nothrow) T` is used.\n\n\n## Example\n\n```cpp\n// BAD: the allocation will throw an unhandled exception\n// instead of returning a null pointer.\nvoid bad1(std::size_t length) noexcept {\n int* dest = new int[length];\n if(!dest) {\n return;\n }\n std::memset(dest, 0, length);\n // ...\n}\n\n// BAD: the allocation won't throw an exception, but\n// instead return a null pointer.\nvoid bad2(std::size_t length) noexcept {\n try {\n int* dest = new(std::nothrow) int[length];\n std::memset(dest, 0, length);\n // ...\n } catch(std::bad_alloc&) {\n // ...\n }\n}\n\n// GOOD: the allocation failure is handled appropriately.\nvoid good1(std::size_t length) noexcept {\n try {\n int* dest = new int[length];\n std::memset(dest, 0, length);\n // ...\n } catch(std::bad_alloc&) {\n // ...\n }\n}\n\n// GOOD: the allocation failure is handled appropriately.\nvoid good2(std::size_t length) noexcept {\n int* dest = new int[length];\n if(!dest) {\n return;\n }\n std::memset(dest, 0, length);\n // ...\n}\n\n```\n\n## References\n* CERT C++ Coding Standard: [MEM52-CPP. Detect and handle memory allocation errors](https://wiki.sei.cmu.edu/confluence/display/cplusplus/MEM52-CPP.+Detect+and+handle+memory+allocation+errors).\n* Common Weakness Enumeration: [CWE-570](https://cwe.mitre.org/data/definitions/570.html).\n* Common Weakness Enumeration: [CWE-252](https://cwe.mitre.org/data/definitions/252.html).\n* Common Weakness Enumeration: [CWE-755](https://cwe.mitre.org/data/definitions/755.html).\n" + }, + "properties" : { + "tags" : [ "correctness", "security", "external/cwe/cwe-570", "external/cwe/cwe-252", "external/cwe/cwe-755" ], + "description" : "Mixing up the failure conditions of 'operator new' and 'operator new(std::nothrow)' can result in unexpected behavior.", + "id" : "cpp/incorrect-allocation-error-handling", + "kind" : "problem", + "name" : "Incorrect allocation-error handling", + "precision" : "medium", + "problem.severity" : "warning", + "security-severity" : "7.5" + } + }, { + "id" : "cpp/unsafe-create-process-call", + "name" : "cpp/unsafe-create-process-call", + "shortDescription" : { + "text" : "NULL application name with an unquoted path in call to CreateProcess" + }, + "fullDescription" : { + "text" : "Calling a function of the CreateProcess* family of functions, where the path contains spaces, introduces a security vulnerability." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# NULL application name with an unquoted path in call to CreateProcess\nThis query indicates that there is a call to a function of the `CreateProcess*` family of functions, which introduces a security vulnerability.\n\n\n## Recommendation\nDo not use `NULL` for the `lpApplicationName` argument to the `CreateProcess*` function.\n\nIf you pass `NULL` for `lpApplicationName`, use quotation marks around the executable path in `lpCommandLine`.\n\n\n## Example\nIn the following example, `CreateProcessW` is called with a `NULL` value for `lpApplicationName`, and the value for `lpCommandLine` that represent the application path is not quoted and has spaces in it.\n\nIf an attacker has access to the file system, they can elevate privileges by creating a file such as `C:\\Program.exe` that will be executed instead of the intended application.\n\n\n```cpp\nSTARTUPINFOW si;\nPROCESS_INFORMATION pi;\n\n// ... \n\nCreateProcessW( // BUG\n NULL, // lpApplicationName\n (LPWSTR)L\"C:\\\\Program Files\\\\MyApp\", // lpCommandLine\n NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);\n\n// ...\n```\nTo fix this issue, specify a valid string for `lpApplicationName`, or quote the path for `lpCommandLine`. For example:\n\n`(LPWSTR)L\"\\\"C:\\\\Program Files\\\\MyApp\\\"\", // lpCommandLine`\n\n\n## References\n* [CreateProcessA function (Microsoft documentation).](https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createprocessa)\n* [CreateProcessW function (Microsoft documentation).](https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createprocessw)\n* [CreateProcessAsUserA function (Microsoft documentation).](https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createprocessasusera)\n* [CreateProcessAsUserW function (Microsoft documentation).](https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createprocessasuserw)\n* [CreateProcessWithLogonW function (Microsoft documentation).](https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createprocesswithlogonw)\n* [CreateProcessWithTokenW function (Microsoft documentation).](https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createprocesswithtokenw)\n* Common Weakness Enumeration: [CWE-428](https://cwe.mitre.org/data/definitions/428.html).\n", + "markdown" : "# NULL application name with an unquoted path in call to CreateProcess\nThis query indicates that there is a call to a function of the `CreateProcess*` family of functions, which introduces a security vulnerability.\n\n\n## Recommendation\nDo not use `NULL` for the `lpApplicationName` argument to the `CreateProcess*` function.\n\nIf you pass `NULL` for `lpApplicationName`, use quotation marks around the executable path in `lpCommandLine`.\n\n\n## Example\nIn the following example, `CreateProcessW` is called with a `NULL` value for `lpApplicationName`, and the value for `lpCommandLine` that represent the application path is not quoted and has spaces in it.\n\nIf an attacker has access to the file system, they can elevate privileges by creating a file such as `C:\\Program.exe` that will be executed instead of the intended application.\n\n\n```cpp\nSTARTUPINFOW si;\nPROCESS_INFORMATION pi;\n\n// ... \n\nCreateProcessW( // BUG\n NULL, // lpApplicationName\n (LPWSTR)L\"C:\\\\Program Files\\\\MyApp\", // lpCommandLine\n NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);\n\n// ...\n```\nTo fix this issue, specify a valid string for `lpApplicationName`, or quote the path for `lpCommandLine`. For example:\n\n`(LPWSTR)L\"\\\"C:\\\\Program Files\\\\MyApp\\\"\", // lpCommandLine`\n\n\n## References\n* [CreateProcessA function (Microsoft documentation).](https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createprocessa)\n* [CreateProcessW function (Microsoft documentation).](https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createprocessw)\n* [CreateProcessAsUserA function (Microsoft documentation).](https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createprocessasusera)\n* [CreateProcessAsUserW function (Microsoft documentation).](https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createprocessasuserw)\n* [CreateProcessWithLogonW function (Microsoft documentation).](https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createprocesswithlogonw)\n* [CreateProcessWithTokenW function (Microsoft documentation).](https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createprocesswithtokenw)\n* Common Weakness Enumeration: [CWE-428](https://cwe.mitre.org/data/definitions/428.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-428" ], + "description" : "Calling a function of the CreateProcess* family of functions, where the path contains spaces, introduces a security vulnerability.", + "id" : "cpp/unsafe-create-process-call", + "kind" : "problem", + "msrc.severity" : "important", + "name" : "NULL application name with an unquoted path in call to CreateProcess", + "precision" : "medium", + "problem.severity" : "error", + "security-severity" : "7.8" + } + }, { + "id" : "cpp/overrunning-write", + "name" : "cpp/overrunning-write", + "shortDescription" : { + "text" : "Potentially overrunning write" + }, + "fullDescription" : { + "text" : "Buffer write operations that do not control the length of data written may overflow." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Potentially overrunning write\nThe program performs a buffer copy or write operation with no upper limit on the size of the copy, and it appears that certain inputs will cause a buffer overflow to occur in this case. In addition to causing program instability, techniques exist which may allow an attacker to use this vulnerability to execute arbitrary code.\n\n\n## Recommendation\nAlways control the length of buffer copy and buffer write operations. `strncpy` should be used over `strcpy`, `snprintf` over `sprintf`, and in other cases 'n-variant' functions should be preferred.\n\n\n## Example\n\n```c\nvoid sayHello(uint32_t userId)\n{\n\tchar buffer[18];\n\n\t// BAD: this message overflows the buffer if userId >= 10000\n\tsprintf(buffer, \"Hello, user %d!\", userId);\n\n\tMessageBox(hWnd, buffer, \"New Message\", MB_OK);\n}\n```\nIn this example, the call to `sprintf` writes a message of 14 characters (including the terminating null) plus the length of the string conversion of \\`userId\\` into a buffer with space for just 18 characters. As such, if \\`userId\\` is greater or equal to \\`10000\\`, the last characters overflow the buffer resulting in undefined behavior.\n\nTo fix this issue these changes should be made:\n\n* Control the size of the buffer by declaring it with a compile time constant.\n* Preferably, replace the call to `sprintf` with `snprintf`, using the defined constant size of the buffer or \\`sizeof(buffer)\\` as maximum length to write. This will prevent the buffer overflow.\n* Optionally, if \\`userId\\` is expected to be less than \\`10000\\`, then return or throw an error if \\`userId\\` is out of bounds.\n* Otherwise, consider increasing the buffer size to at least 25 characters, so that the message is displayed correctly regardless of the value of \\`userId\\`.\n\n## References\n* CERT C Coding Standard: [STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator](https://www.securecoding.cert.org/confluence/display/c/STR31-C.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator).\n* CERT C++ Coding Standard: [STR50-CPP. Guarantee that storage for strings has sufficient space for character data and the null terminator](https://www.securecoding.cert.org/confluence/display/cplusplus/STR50-CPP.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator).\n* Common Weakness Enumeration: [CWE-120](https://cwe.mitre.org/data/definitions/120.html).\n* Common Weakness Enumeration: [CWE-787](https://cwe.mitre.org/data/definitions/787.html).\n* Common Weakness Enumeration: [CWE-805](https://cwe.mitre.org/data/definitions/805.html).\n", + "markdown" : "# Potentially overrunning write\nThe program performs a buffer copy or write operation with no upper limit on the size of the copy, and it appears that certain inputs will cause a buffer overflow to occur in this case. In addition to causing program instability, techniques exist which may allow an attacker to use this vulnerability to execute arbitrary code.\n\n\n## Recommendation\nAlways control the length of buffer copy and buffer write operations. `strncpy` should be used over `strcpy`, `snprintf` over `sprintf`, and in other cases 'n-variant' functions should be preferred.\n\n\n## Example\n\n```c\nvoid sayHello(uint32_t userId)\n{\n\tchar buffer[18];\n\n\t// BAD: this message overflows the buffer if userId >= 10000\n\tsprintf(buffer, \"Hello, user %d!\", userId);\n\n\tMessageBox(hWnd, buffer, \"New Message\", MB_OK);\n}\n```\nIn this example, the call to `sprintf` writes a message of 14 characters (including the terminating null) plus the length of the string conversion of \\`userId\\` into a buffer with space for just 18 characters. As such, if \\`userId\\` is greater or equal to \\`10000\\`, the last characters overflow the buffer resulting in undefined behavior.\n\nTo fix this issue these changes should be made:\n\n* Control the size of the buffer by declaring it with a compile time constant.\n* Preferably, replace the call to `sprintf` with `snprintf`, using the defined constant size of the buffer or \\`sizeof(buffer)\\` as maximum length to write. This will prevent the buffer overflow.\n* Optionally, if \\`userId\\` is expected to be less than \\`10000\\`, then return or throw an error if \\`userId\\` is out of bounds.\n* Otherwise, consider increasing the buffer size to at least 25 characters, so that the message is displayed correctly regardless of the value of \\`userId\\`.\n\n## References\n* CERT C Coding Standard: [STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator](https://www.securecoding.cert.org/confluence/display/c/STR31-C.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator).\n* CERT C++ Coding Standard: [STR50-CPP. Guarantee that storage for strings has sufficient space for character data and the null terminator](https://www.securecoding.cert.org/confluence/display/cplusplus/STR50-CPP.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator).\n* Common Weakness Enumeration: [CWE-120](https://cwe.mitre.org/data/definitions/120.html).\n* Common Weakness Enumeration: [CWE-787](https://cwe.mitre.org/data/definitions/787.html).\n* Common Weakness Enumeration: [CWE-805](https://cwe.mitre.org/data/definitions/805.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "security", "external/cwe/cwe-120", "external/cwe/cwe-787", "external/cwe/cwe-805" ], + "description" : "Buffer write operations that do not control the length\n of data written may overflow.", + "id" : "cpp/overrunning-write", + "kind" : "problem", + "name" : "Potentially overrunning write", + "precision" : "medium", + "problem.severity" : "error", + "security-severity" : "9.3" + } + }, { + "id" : "cpp/unbounded-write", + "name" : "cpp/unbounded-write", + "shortDescription" : { + "text" : "Unbounded write" + }, + "fullDescription" : { + "text" : "Buffer write operations that do not control the length of data written may overflow." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Unbounded write\nThe program performs a buffer copy or write operation with no upper limit on the size of the copy. An unexpectedly long input that reaches this code will cause the buffer to overflow. In addition to causing program instability, techniques exist which may allow an attacker to use this vulnerability to execute arbitrary code.\n\n\n## Recommendation\nAlways control the length of buffer copy and buffer write operations. `strncpy` should be used over `strcpy`, `snprintf` over `sprintf`, and in other cases 'n-variant' functions should be preferred.\n\n\n## Example\n\n```c\nvoid congratulateUser(const char *userName)\n{\n\tchar buffer[80];\n\n\t// BAD: this could overflow the buffer if the UserName is long\n\tsprintf(buffer, \"Congratulations, %s!\", userName);\n\n\tMessageBox(hWnd, buffer, \"New Message\", MB_OK);\n}\n```\nIn this example, the call to `sprintf` may overflow `buffer`. This occurs if the argument `userName` is very long, such that the resulting string is more than the 80 characters allowed.\n\nTo fix the problem the call to `sprintf` should be replaced with `snprintf`, specifying a maximum length of 80 characters.\n\n\n## References\n* CERT C Coding Standard: [STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator](https://www.securecoding.cert.org/confluence/display/c/STR31-C.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator).\n* CERT C++ Coding Standard: [STR50-CPP. Guarantee that storage for strings has sufficient space for character data and the null terminator](https://www.securecoding.cert.org/confluence/display/cplusplus/STR50-CPP.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator).\n* Common Weakness Enumeration: [CWE-120](https://cwe.mitre.org/data/definitions/120.html).\n* Common Weakness Enumeration: [CWE-787](https://cwe.mitre.org/data/definitions/787.html).\n* Common Weakness Enumeration: [CWE-805](https://cwe.mitre.org/data/definitions/805.html).\n", + "markdown" : "# Unbounded write\nThe program performs a buffer copy or write operation with no upper limit on the size of the copy. An unexpectedly long input that reaches this code will cause the buffer to overflow. In addition to causing program instability, techniques exist which may allow an attacker to use this vulnerability to execute arbitrary code.\n\n\n## Recommendation\nAlways control the length of buffer copy and buffer write operations. `strncpy` should be used over `strcpy`, `snprintf` over `sprintf`, and in other cases 'n-variant' functions should be preferred.\n\n\n## Example\n\n```c\nvoid congratulateUser(const char *userName)\n{\n\tchar buffer[80];\n\n\t// BAD: this could overflow the buffer if the UserName is long\n\tsprintf(buffer, \"Congratulations, %s!\", userName);\n\n\tMessageBox(hWnd, buffer, \"New Message\", MB_OK);\n}\n```\nIn this example, the call to `sprintf` may overflow `buffer`. This occurs if the argument `userName` is very long, such that the resulting string is more than the 80 characters allowed.\n\nTo fix the problem the call to `sprintf` should be replaced with `snprintf`, specifying a maximum length of 80 characters.\n\n\n## References\n* CERT C Coding Standard: [STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator](https://www.securecoding.cert.org/confluence/display/c/STR31-C.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator).\n* CERT C++ Coding Standard: [STR50-CPP. Guarantee that storage for strings has sufficient space for character data and the null terminator](https://www.securecoding.cert.org/confluence/display/cplusplus/STR50-CPP.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator).\n* Common Weakness Enumeration: [CWE-120](https://cwe.mitre.org/data/definitions/120.html).\n* Common Weakness Enumeration: [CWE-787](https://cwe.mitre.org/data/definitions/787.html).\n* Common Weakness Enumeration: [CWE-805](https://cwe.mitre.org/data/definitions/805.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "security", "external/cwe/cwe-120", "external/cwe/cwe-787", "external/cwe/cwe-805" ], + "description" : "Buffer write operations that do not control the length\n of data written may overflow.", + "id" : "cpp/unbounded-write", + "kind" : "path-problem", + "name" : "Unbounded write", + "precision" : "medium", + "problem.severity" : "error", + "security-severity" : "9.3" + } + }, { + "id" : "cpp/overrunning-write-with-float", + "name" : "cpp/overrunning-write-with-float", + "shortDescription" : { + "text" : "Potentially overrunning write with float to string conversion" + }, + "fullDescription" : { + "text" : "Buffer write operations that do not control the length of data written may overflow when floating point inputs take extreme values." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Potentially overrunning write with float to string conversion\nThe program performs a buffer copy or write operation that includes one or more float to string conversions (i.e. the %f format specifier), which may overflow the destination buffer if extreme inputs are given. In addition to causing program instability, techniques exist which may allow an attacker to use this vulnerability to execute arbitrary code.\n\n\n## Recommendation\nAlways control the length of buffer copy and buffer write operations. `strncpy` should be used over `strcpy`, `snprintf` over `sprintf`, and in other cases 'n-variant' functions should be preferred.\n\n\n## Example\n\n```c\nvoid displayValue(double value)\n{\n\tchar buffer[256];\n\n\t// BAD: extreme values may overflow the buffer\n\tsprintf(buffer, \"%f\", value);\n\n\tMessageBox(hWnd, buffer, \"A Number\", MB_OK);\n}\n```\nIn this example, the call to `sprintf` contains a `%f` format specifier. Though a 256 character buffer has been allowed, it is not sufficient for the most extreme floating point inputs. For example the representation of double value 1e304 (that is 1 with 304 zeroes after it) will overflow a buffer of this length.\n\nTo fix this issue three changes should be made:\n\n* Control the size of the buffer using a preprocessor define.\n* Replace the call to `sprintf` with `snprintf`, specifying the define as the maximum length to copy. This will prevent the buffer overflow.\n* Consider using the `%g` format specifier instead of `%f`.\n\n## References\n* CERT C Coding Standard: [STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator](https://www.securecoding.cert.org/confluence/display/c/STR31-C.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator).\n* CERT C++ Coding Standard: [STR50-CPP. Guarantee that storage for strings has sufficient space for character data and the null terminator](https://www.securecoding.cert.org/confluence/display/cplusplus/STR50-CPP.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator).\n* Common Weakness Enumeration: [CWE-120](https://cwe.mitre.org/data/definitions/120.html).\n* Common Weakness Enumeration: [CWE-787](https://cwe.mitre.org/data/definitions/787.html).\n* Common Weakness Enumeration: [CWE-805](https://cwe.mitre.org/data/definitions/805.html).\n", + "markdown" : "# Potentially overrunning write with float to string conversion\nThe program performs a buffer copy or write operation that includes one or more float to string conversions (i.e. the %f format specifier), which may overflow the destination buffer if extreme inputs are given. In addition to causing program instability, techniques exist which may allow an attacker to use this vulnerability to execute arbitrary code.\n\n\n## Recommendation\nAlways control the length of buffer copy and buffer write operations. `strncpy` should be used over `strcpy`, `snprintf` over `sprintf`, and in other cases 'n-variant' functions should be preferred.\n\n\n## Example\n\n```c\nvoid displayValue(double value)\n{\n\tchar buffer[256];\n\n\t// BAD: extreme values may overflow the buffer\n\tsprintf(buffer, \"%f\", value);\n\n\tMessageBox(hWnd, buffer, \"A Number\", MB_OK);\n}\n```\nIn this example, the call to `sprintf` contains a `%f` format specifier. Though a 256 character buffer has been allowed, it is not sufficient for the most extreme floating point inputs. For example the representation of double value 1e304 (that is 1 with 304 zeroes after it) will overflow a buffer of this length.\n\nTo fix this issue three changes should be made:\n\n* Control the size of the buffer using a preprocessor define.\n* Replace the call to `sprintf` with `snprintf`, specifying the define as the maximum length to copy. This will prevent the buffer overflow.\n* Consider using the `%g` format specifier instead of `%f`.\n\n## References\n* CERT C Coding Standard: [STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator](https://www.securecoding.cert.org/confluence/display/c/STR31-C.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator).\n* CERT C++ Coding Standard: [STR50-CPP. Guarantee that storage for strings has sufficient space for character data and the null terminator](https://www.securecoding.cert.org/confluence/display/cplusplus/STR50-CPP.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator).\n* Common Weakness Enumeration: [CWE-120](https://cwe.mitre.org/data/definitions/120.html).\n* Common Weakness Enumeration: [CWE-787](https://cwe.mitre.org/data/definitions/787.html).\n* Common Weakness Enumeration: [CWE-805](https://cwe.mitre.org/data/definitions/805.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "security", "external/cwe/cwe-120", "external/cwe/cwe-787", "external/cwe/cwe-805" ], + "description" : "Buffer write operations that do not control the length\n of data written may overflow when floating point inputs\n take extreme values.", + "id" : "cpp/overrunning-write-with-float", + "kind" : "problem", + "name" : "Potentially overrunning write with float to string conversion", + "precision" : "medium", + "problem.severity" : "error", + "security-severity" : "9.3" + } + }, { + "id" : "cpp/potential-system-data-exposure", + "name" : "cpp/potential-system-data-exposure", + "shortDescription" : { + "text" : "Potential exposure of sensitive system data to an unauthorized control sphere" + }, + "fullDescription" : { + "text" : "Exposing sensitive system data helps a malicious user learn about the system and form an attack plan." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Potential exposure of sensitive system data to an unauthorized control sphere\nExposing system data or debugging information may help a malicious user learn about the system and form an attack plan. An attacker can use error messages that reveal technologies, operating systems, and product versions to tune their attack against known vulnerabilities in the software.\n\nThis query finds locations where system configuration information that is particularly sensitive might be revealed to a user.\n\n\n## Recommendation\nDo not expose system configuration information to users. Be wary of the difference between information that could be helpful to users, and unnecessary details that could be useful to a malicious user.\n\n\n## Example\nIn this example the value of the `PATH` environment variable is revealed in full to the user when a particular error occurs. This might reveal information such as the software installed on your system to a malicious user who does not have legitimate access to that information.\n\n\n```cpp\nchar* key = getenv(\"APP_KEY\");\n\n//...\n\nfprintf(stderr, \"Key not recognized: %s\\n\", key);\n```\nThe message should be rephrased without this information, for example:\n\n\n```cpp\nchar* key = getenv(\"APP_KEY\");\n\n//...\n\nfprintf(stderr, \"Application key not recognized. Please ensure the key is correct or contact a system administrator.\\n\", key);\n```\n\n## References\n* Common Weakness Enumeration: [CWE-497](https://cwe.mitre.org/data/definitions/497.html).\n", + "markdown" : "# Potential exposure of sensitive system data to an unauthorized control sphere\nExposing system data or debugging information may help a malicious user learn about the system and form an attack plan. An attacker can use error messages that reveal technologies, operating systems, and product versions to tune their attack against known vulnerabilities in the software.\n\nThis query finds locations where system configuration information that is particularly sensitive might be revealed to a user.\n\n\n## Recommendation\nDo not expose system configuration information to users. Be wary of the difference between information that could be helpful to users, and unnecessary details that could be useful to a malicious user.\n\n\n## Example\nIn this example the value of the `PATH` environment variable is revealed in full to the user when a particular error occurs. This might reveal information such as the software installed on your system to a malicious user who does not have legitimate access to that information.\n\n\n```cpp\nchar* key = getenv(\"APP_KEY\");\n\n//...\n\nfprintf(stderr, \"Key not recognized: %s\\n\", key);\n```\nThe message should be rephrased without this information, for example:\n\n\n```cpp\nchar* key = getenv(\"APP_KEY\");\n\n//...\n\nfprintf(stderr, \"Application key not recognized. Please ensure the key is correct or contact a system administrator.\\n\", key);\n```\n\n## References\n* Common Weakness Enumeration: [CWE-497](https://cwe.mitre.org/data/definitions/497.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-497" ], + "description" : "Exposing sensitive system data helps\n a malicious user learn about the system and form an\n attack plan.", + "id" : "cpp/potential-system-data-exposure", + "kind" : "path-problem", + "name" : "Potential exposure of sensitive system data to an unauthorized control sphere", + "precision" : "medium", + "problem.severity" : "warning", + "security-severity" : "6.5" + } + }, { + "id" : "cpp/cleartext-storage-buffer", + "name" : "cpp/cleartext-storage-buffer", + "shortDescription" : { + "text" : "Cleartext storage of sensitive information in buffer" + }, + "fullDescription" : { + "text" : "Storing sensitive information in cleartext can expose it to an attacker." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Cleartext storage of sensitive information in buffer\nSensitive information that is stored unencrypted is accessible to an attacker who gains access to the storage.\n\n\n## Recommendation\nEnsure that sensitive information is always encrypted before being stored to a file or transmitted over the network. It may be wise to encrypt information before it is put into a buffer that may be readable in memory.\n\nIn general, decrypt sensitive information only at the point where it is necessary for it to be used in cleartext.\n\n\n## Example\nThe following example shows two ways of storing user credentials in a file. In the 'BAD' case, the credentials are simply stored in cleartext. In the 'GOOD' case, the credentials are encrypted before storing them.\n\n\n```c\nvoid writeCredentials() {\n char *password = \"cleartext password\";\n FILE* file = fopen(\"credentials.txt\", \"w\");\n \n // BAD: write password to disk in cleartext\n fputs(password, file);\n \n // GOOD: encrypt password first\n char *encrypted = encrypt(password);\n fputs(encrypted, file);\n}\n\n\n```\n\n## References\n* M. Dowd, J. McDonald and J. Schuhm, *The Art of Software Security Assessment*, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.\n* M. Howard and D. LeBlanc, *Writing Secure Code*, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.\n* Common Weakness Enumeration: [CWE-312](https://cwe.mitre.org/data/definitions/312.html).\n", + "markdown" : "# Cleartext storage of sensitive information in buffer\nSensitive information that is stored unencrypted is accessible to an attacker who gains access to the storage.\n\n\n## Recommendation\nEnsure that sensitive information is always encrypted before being stored to a file or transmitted over the network. It may be wise to encrypt information before it is put into a buffer that may be readable in memory.\n\nIn general, decrypt sensitive information only at the point where it is necessary for it to be used in cleartext.\n\n\n## Example\nThe following example shows two ways of storing user credentials in a file. In the 'BAD' case, the credentials are simply stored in cleartext. In the 'GOOD' case, the credentials are encrypted before storing them.\n\n\n```c\nvoid writeCredentials() {\n char *password = \"cleartext password\";\n FILE* file = fopen(\"credentials.txt\", \"w\");\n \n // BAD: write password to disk in cleartext\n fputs(password, file);\n \n // GOOD: encrypt password first\n char *encrypted = encrypt(password);\n fputs(encrypted, file);\n}\n\n\n```\n\n## References\n* M. Dowd, J. McDonald and J. Schuhm, *The Art of Software Security Assessment*, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.\n* M. Howard and D. LeBlanc, *Writing Secure Code*, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.\n* Common Weakness Enumeration: [CWE-312](https://cwe.mitre.org/data/definitions/312.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-312" ], + "description" : "Storing sensitive information in cleartext can expose it\n to an attacker.", + "id" : "cpp/cleartext-storage-buffer", + "kind" : "path-problem", + "name" : "Cleartext storage of sensitive information in buffer", + "precision" : "medium", + "problem.severity" : "warning", + "security-severity" : "7.5" + } + }, { + "id" : "cpp/uncontrolled-process-operation", + "name" : "cpp/uncontrolled-process-operation", + "shortDescription" : { + "text" : "Uncontrolled process operation" + }, + "fullDescription" : { + "text" : "Using externally controlled strings in a process operation can allow an attacker to execute malicious commands." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Uncontrolled process operation\nThe code passes user input directly to `system`, `dlopen`, `LoadLibrary` or some other process or library routine. As a result, the user can cause execution of arbitrary code.\n\n\n## Recommendation\nIf possible, use hard-coded string literals for the command to run or library to load. Instead of passing the user input directly to the process or library function, examine the user input and then choose among hard-coded string literals.\n\nIf the applicable libraries or commands cannot be determined at compile time, then add code to verify that the user-input string is safe before using it.\n\n\n## Example\n\n```c\nint main(int argc, char** argv) {\n char *lib = argv[2];\n \n // BAD: the user can cause arbitrary code to be loaded\n void* handle = dlopen(lib, RTLD_LAZY);\n \n // GOOD: only hard-coded libraries can be loaded\n void* handle2;\n\n if (!strcmp(lib, \"inmem\")) {\n handle2 = dlopen(\"/usr/share/dbwrap/inmem\", RTLD_LAZY);\n } else if (!strcmp(lib, \"mysql\")) {\n handle2 = dlopen(\"/usr/share/dbwrap/mysql\", RTLD_LAZY);\n } else {\n die(\"Invalid library specified\\n\");\n }\n}\n\n```\n\n## References\n* Common Weakness Enumeration: [CWE-114](https://cwe.mitre.org/data/definitions/114.html).\n", + "markdown" : "# Uncontrolled process operation\nThe code passes user input directly to `system`, `dlopen`, `LoadLibrary` or some other process or library routine. As a result, the user can cause execution of arbitrary code.\n\n\n## Recommendation\nIf possible, use hard-coded string literals for the command to run or library to load. Instead of passing the user input directly to the process or library function, examine the user input and then choose among hard-coded string literals.\n\nIf the applicable libraries or commands cannot be determined at compile time, then add code to verify that the user-input string is safe before using it.\n\n\n## Example\n\n```c\nint main(int argc, char** argv) {\n char *lib = argv[2];\n \n // BAD: the user can cause arbitrary code to be loaded\n void* handle = dlopen(lib, RTLD_LAZY);\n \n // GOOD: only hard-coded libraries can be loaded\n void* handle2;\n\n if (!strcmp(lib, \"inmem\")) {\n handle2 = dlopen(\"/usr/share/dbwrap/inmem\", RTLD_LAZY);\n } else if (!strcmp(lib, \"mysql\")) {\n handle2 = dlopen(\"/usr/share/dbwrap/mysql\", RTLD_LAZY);\n } else {\n die(\"Invalid library specified\\n\");\n }\n}\n\n```\n\n## References\n* Common Weakness Enumeration: [CWE-114](https://cwe.mitre.org/data/definitions/114.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-114" ], + "description" : "Using externally controlled strings in a process\n operation can allow an attacker to execute malicious\n commands.", + "id" : "cpp/uncontrolled-process-operation", + "kind" : "path-problem", + "name" : "Uncontrolled process operation", + "precision" : "medium", + "problem.severity" : "warning", + "security-severity" : "8.2" + } + }, { + "id" : "cpp/user-controlled-bypass", + "name" : "cpp/user-controlled-bypass", + "shortDescription" : { + "text" : "Authentication bypass by spoofing" + }, + "fullDescription" : { + "text" : "Authentication by checking that the peer's address matches a known IP or web address is unsafe as it is vulnerable to spoofing attacks." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Authentication bypass by spoofing\nCode which relies on an IP address or domain name for authentication can be exploited by an attacker who spoofs their address.\n\n\n## Recommendation\nIP address verification can be a useful part of an authentication scheme, but it should not be the single factor required for authentication. Make sure that other authentication methods are also in place.\n\n\n## Example\nIn this example (taken from [CWE-290: Authentication Bypass by Spoofing](http://cwe.mitre.org/data/definitions/290.html)), the client is authenticated by checking that its IP address is `127.0.0.1`. An attacker might be able to bypass this authentication by spoofing their IP address.\n\n\n```cpp\n\n#define BUFFER_SIZE (4 * 1024)\n\nvoid receiveData()\n{\n int sock;\n sockaddr_in addr, addr_from;\n char buffer[BUFFER_SIZE];\n int msg_size;\n socklen_t addr_from_len;\n\n // configure addr\n memset(&addr, 0, sizeof(addr));\n addr.sin_family = AF_INET;\n addr.sin_port = htons(1234);\n addr.sin_addr.s_addr = INADDR_ANY;\n\n // create and bind the socket\n sock = socket(AF_INET, SOCK_DGRAM, 0);\n bind(sock, (sockaddr *)&addr, sizeof(addr));\n\n // receive message\n addr_from_len = sizeof(addr_from);\n msg_size = recvfrom(sock, buffer, BUFFER_SIZE, 0, (sockaddr *)&addr_from, &addr_from_len);\n\n // BAD: the address is controllable by the user, so it\n // could be spoofed to bypass the security check below.\n if ((msg_size > 0) && (strcmp(\"127.0.0.1\", inet_ntoa(addr_from.sin_addr)) == 0))\n {\n // ...\n }\n}\n\n```\n\n## References\n* Common Weakness Enumeration: [CWE-290](https://cwe.mitre.org/data/definitions/290.html).\n", + "markdown" : "# Authentication bypass by spoofing\nCode which relies on an IP address or domain name for authentication can be exploited by an attacker who spoofs their address.\n\n\n## Recommendation\nIP address verification can be a useful part of an authentication scheme, but it should not be the single factor required for authentication. Make sure that other authentication methods are also in place.\n\n\n## Example\nIn this example (taken from [CWE-290: Authentication Bypass by Spoofing](http://cwe.mitre.org/data/definitions/290.html)), the client is authenticated by checking that its IP address is `127.0.0.1`. An attacker might be able to bypass this authentication by spoofing their IP address.\n\n\n```cpp\n\n#define BUFFER_SIZE (4 * 1024)\n\nvoid receiveData()\n{\n int sock;\n sockaddr_in addr, addr_from;\n char buffer[BUFFER_SIZE];\n int msg_size;\n socklen_t addr_from_len;\n\n // configure addr\n memset(&addr, 0, sizeof(addr));\n addr.sin_family = AF_INET;\n addr.sin_port = htons(1234);\n addr.sin_addr.s_addr = INADDR_ANY;\n\n // create and bind the socket\n sock = socket(AF_INET, SOCK_DGRAM, 0);\n bind(sock, (sockaddr *)&addr, sizeof(addr));\n\n // receive message\n addr_from_len = sizeof(addr_from);\n msg_size = recvfrom(sock, buffer, BUFFER_SIZE, 0, (sockaddr *)&addr_from, &addr_from_len);\n\n // BAD: the address is controllable by the user, so it\n // could be spoofed to bypass the security check below.\n if ((msg_size > 0) && (strcmp(\"127.0.0.1\", inet_ntoa(addr_from.sin_addr)) == 0))\n {\n // ...\n }\n}\n\n```\n\n## References\n* Common Weakness Enumeration: [CWE-290](https://cwe.mitre.org/data/definitions/290.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-290" ], + "description" : "Authentication by checking that the peer's address\n matches a known IP or web address is unsafe as it is\n vulnerable to spoofing attacks.", + "id" : "cpp/user-controlled-bypass", + "kind" : "path-problem", + "name" : "Authentication bypass by spoofing", + "precision" : "medium", + "problem.severity" : "warning", + "security-severity" : "8.1" + } + }, { + "id" : "cpp/path-injection", + "name" : "cpp/path-injection", + "shortDescription" : { + "text" : "Uncontrolled data used in path expression" + }, + "fullDescription" : { + "text" : "Accessing paths influenced by users can allow an attacker to access unexpected resources." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Uncontrolled data used in path expression\nAccessing paths controlled by users can allow an attacker to access unexpected resources. This can result in sensitive information being revealed or deleted, or an attacker being able to influence behavior by modifying unexpected files.\n\nPaths that are naively constructed from data controlled by a user may contain unexpected special characters, such as \"..\". Such a path may potentially point to any directory on the filesystem.\n\n\n## Recommendation\nValidate user input before using it to construct a filepath. Ideally, follow these rules:\n\n* Do not allow more than a single \".\" character.\n* Do not allow directory separators such as \"/\" or \"\\\\\" (depending on the filesystem).\n* Do not rely on simply replacing problematic sequences such as \"../\". For example, after applying this filter to \".../...//\" the resulting string would still be \"../\".\n* Ideally use a whitelist of known good patterns.\n\n## Example\nIn this example, a username and file are read from the arguments to main and then used to access a file in the user's home directory. However, a malicious user could enter a filename which contains special characters. For example, the string \"../../etc/passwd\" will result in the code reading the file located at \"/home/\\[user\\]/../../etc/passwd\", which is the system's password file. This could potentially allow them to access all the system's passwords.\n\n\n```c\nint main(int argc, char** argv) {\n char *userAndFile = argv[2];\n \n {\n char fileBuffer[FILENAME_MAX] = \"/home/\";\n char *fileName = fileBuffer;\n size_t len = strlen(fileName);\n strncat(fileName+len, userAndFile, FILENAME_MAX-len-1);\n // BAD: a string from the user is used in a filename\n fopen(fileName, \"wb+\");\n }\n\n {\n char fileBuffer[FILENAME_MAX] = \"/home/\";\n char *fileName = fileBuffer;\n size_t len = strlen(fileName);\n // GOOD: use a fixed file\n char* fixed = \"jim/file.txt\";\n strncat(fileName+len, fixed, FILENAME_MAX-len-1);\n fopen(fileName, \"wb+\");\n }\n}\n\n```\n\n## References\n* OWASP: [Path Traversal](https://owasp.org/www-community/attacks/Path_Traversal).\n* Common Weakness Enumeration: [CWE-22](https://cwe.mitre.org/data/definitions/22.html).\n* Common Weakness Enumeration: [CWE-23](https://cwe.mitre.org/data/definitions/23.html).\n* Common Weakness Enumeration: [CWE-36](https://cwe.mitre.org/data/definitions/36.html).\n* Common Weakness Enumeration: [CWE-73](https://cwe.mitre.org/data/definitions/73.html).\n", + "markdown" : "# Uncontrolled data used in path expression\nAccessing paths controlled by users can allow an attacker to access unexpected resources. This can result in sensitive information being revealed or deleted, or an attacker being able to influence behavior by modifying unexpected files.\n\nPaths that are naively constructed from data controlled by a user may contain unexpected special characters, such as \"..\". Such a path may potentially point to any directory on the filesystem.\n\n\n## Recommendation\nValidate user input before using it to construct a filepath. Ideally, follow these rules:\n\n* Do not allow more than a single \".\" character.\n* Do not allow directory separators such as \"/\" or \"\\\\\" (depending on the filesystem).\n* Do not rely on simply replacing problematic sequences such as \"../\". For example, after applying this filter to \".../...//\" the resulting string would still be \"../\".\n* Ideally use a whitelist of known good patterns.\n\n## Example\nIn this example, a username and file are read from the arguments to main and then used to access a file in the user's home directory. However, a malicious user could enter a filename which contains special characters. For example, the string \"../../etc/passwd\" will result in the code reading the file located at \"/home/\\[user\\]/../../etc/passwd\", which is the system's password file. This could potentially allow them to access all the system's passwords.\n\n\n```c\nint main(int argc, char** argv) {\n char *userAndFile = argv[2];\n \n {\n char fileBuffer[FILENAME_MAX] = \"/home/\";\n char *fileName = fileBuffer;\n size_t len = strlen(fileName);\n strncat(fileName+len, userAndFile, FILENAME_MAX-len-1);\n // BAD: a string from the user is used in a filename\n fopen(fileName, \"wb+\");\n }\n\n {\n char fileBuffer[FILENAME_MAX] = \"/home/\";\n char *fileName = fileBuffer;\n size_t len = strlen(fileName);\n // GOOD: use a fixed file\n char* fixed = \"jim/file.txt\";\n strncat(fileName+len, fixed, FILENAME_MAX-len-1);\n fopen(fileName, \"wb+\");\n }\n}\n\n```\n\n## References\n* OWASP: [Path Traversal](https://owasp.org/www-community/attacks/Path_Traversal).\n* Common Weakness Enumeration: [CWE-22](https://cwe.mitre.org/data/definitions/22.html).\n* Common Weakness Enumeration: [CWE-23](https://cwe.mitre.org/data/definitions/23.html).\n* Common Weakness Enumeration: [CWE-36](https://cwe.mitre.org/data/definitions/36.html).\n* Common Weakness Enumeration: [CWE-73](https://cwe.mitre.org/data/definitions/73.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-022", "external/cwe/cwe-023", "external/cwe/cwe-036", "external/cwe/cwe-073" ], + "description" : "Accessing paths influenced by users can allow an\n attacker to access unexpected resources.", + "id" : "cpp/path-injection", + "kind" : "path-problem", + "name" : "Uncontrolled data used in path expression", + "precision" : "medium", + "problem.severity" : "warning", + "security-severity" : "7.5" + } + }, { + "id" : "cpp/uncontrolled-allocation-size", + "name" : "cpp/uncontrolled-allocation-size", + "shortDescription" : { + "text" : "Overflow in uncontrolled allocation size" + }, + "fullDescription" : { + "text" : "Allocating memory with a size controlled by an external user can result in integer overflow." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Overflow in uncontrolled allocation size\nThis code calculates an allocation size by multiplying a user input by a `sizeof` expression. Since the user input has no apparent guard on its magnitude, this multiplication can overflow. When an integer multiply overflows in C, the result can wrap around and be much smaller than intended. A later attempt to put data into the allocated buffer can then overflow.\n\n\n## Recommendation\nGuard all integer parameters that come from an external user. Implement a guard with the expected range for the parameter and make sure that the input value meets both the minimum and maximum requirements for this range. If the input value fails this guard then reject the request before proceeding further. If the input value passes the guard then subsequent calculations should not overflow.\n\n\n## Example\n\n```c\nint factor = atoi(getenv(\"BRANCHING_FACTOR\"));\n\n// GOOD: Prevent overflow by checking the input\nif (factor < 0 || factor > 1000) {\n log(\"Factor out of range (%d)\\n\", factor);\n return -1;\n}\n\n// This line can allocate too little memory if factor\n// is very large.\nchar **root_node = (char **) malloc(factor * sizeof(char *));\n\n```\nThis code shows one way to guard that an input value is within the expected range. If `factor` fails the guard, then an error is returned, and the value is not used as an argument to the subsequent call to `malloc`. Without this guard, the allocated buffer might be too small to hold the data intended for it.\n\n\n## References\n* The CERT Oracle Secure Coding Standard for C: [INT04-C. Enforce limits on integer values originating from tainted sources](https://www.securecoding.cert.org/confluence/display/c/INT04-C.+Enforce+limits+on+integer+values+originating+from+tainted+sources).\n* Common Weakness Enumeration: [CWE-190](https://cwe.mitre.org/data/definitions/190.html).\n* Common Weakness Enumeration: [CWE-789](https://cwe.mitre.org/data/definitions/789.html).\n", + "markdown" : "# Overflow in uncontrolled allocation size\nThis code calculates an allocation size by multiplying a user input by a `sizeof` expression. Since the user input has no apparent guard on its magnitude, this multiplication can overflow. When an integer multiply overflows in C, the result can wrap around and be much smaller than intended. A later attempt to put data into the allocated buffer can then overflow.\n\n\n## Recommendation\nGuard all integer parameters that come from an external user. Implement a guard with the expected range for the parameter and make sure that the input value meets both the minimum and maximum requirements for this range. If the input value fails this guard then reject the request before proceeding further. If the input value passes the guard then subsequent calculations should not overflow.\n\n\n## Example\n\n```c\nint factor = atoi(getenv(\"BRANCHING_FACTOR\"));\n\n// GOOD: Prevent overflow by checking the input\nif (factor < 0 || factor > 1000) {\n log(\"Factor out of range (%d)\\n\", factor);\n return -1;\n}\n\n// This line can allocate too little memory if factor\n// is very large.\nchar **root_node = (char **) malloc(factor * sizeof(char *));\n\n```\nThis code shows one way to guard that an input value is within the expected range. If `factor` fails the guard, then an error is returned, and the value is not used as an argument to the subsequent call to `malloc`. Without this guard, the allocated buffer might be too small to hold the data intended for it.\n\n\n## References\n* The CERT Oracle Secure Coding Standard for C: [INT04-C. Enforce limits on integer values originating from tainted sources](https://www.securecoding.cert.org/confluence/display/c/INT04-C.+Enforce+limits+on+integer+values+originating+from+tainted+sources).\n* Common Weakness Enumeration: [CWE-190](https://cwe.mitre.org/data/definitions/190.html).\n* Common Weakness Enumeration: [CWE-789](https://cwe.mitre.org/data/definitions/789.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "security", "external/cwe/cwe-190", "external/cwe/cwe-789" ], + "description" : "Allocating memory with a size controlled by an external\n user can result in integer overflow.", + "id" : "cpp/uncontrolled-allocation-size", + "kind" : "path-problem", + "name" : "Overflow in uncontrolled allocation size", + "precision" : "medium", + "problem.severity" : "error", + "security-severity" : "8.1" + } + }, { + "id" : "cpp/cleartext-storage-database", + "name" : "cpp/cleartext-storage-database", + "shortDescription" : { + "text" : "Cleartext storage of sensitive information in an SQLite database" + }, + "fullDescription" : { + "text" : "Storing sensitive information in a non-encrypted database can expose it to an attacker." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Cleartext storage of sensitive information in an SQLite database\nSensitive information that is stored in an unencrypted SQLite database is accessible to an attacker who gains access to the database.\n\n\n## Recommendation\nEnsure that if sensitive information is stored in a database then the database is always encrypted.\n\n\n## Example\nThe following example shows two ways of storing information in an SQLite database. In the 'BAD' case, the credentials are simply stored in cleartext. In the 'GOOD' case, the database (and thus the credentials) are encrypted.\n\n\n```c\n\nvoid bad(void) {\n char *password = \"cleartext password\";\n sqlite3 *credentialsDB;\n sqlite3_stmt *stmt;\n\n if (sqlite3_open(\"credentials.db\", &credentialsDB) == SQLITE_OK) {\n // BAD: database opened without encryption being enabled\n sqlite3_exec(credentialsDB, \"CREATE TABLE IF NOT EXISTS creds (password TEXT);\", NULL, NULL, NULL);\n if (sqlite3_prepare_v2(credentialsDB, \"INSERT INTO creds(password) VALUES(?)\", -1, &stmt, NULL) == SQLITE_OK) {\n sqlite3_bind_text(stmt, 1, password, -1, SQLITE_TRANSIENT);\n sqlite3_step(stmt);\n sqlite3_finalize(stmt);\n sqlite3_close(credentialsDB);\n }\n }\n}\n\nvoid good(void) {\n char *password = \"cleartext password\";\n sqlite3 *credentialsDB;\n sqlite3_stmt *stmt;\n\n if (sqlite3_open(\"credentials.db\", &credentialsDB) == SQLITE_OK) {\n // GOOD: database encryption enabled:\n sqlite3_exec(credentialsDB, \"PRAGMA key = 'secretKey!'\", NULL, NULL, NULL);\n sqlite3_exec(credentialsDB, \"CREATE TABLE IF NOT EXISTS creds (password TEXT);\", NULL, NULL, NULL);\n if (sqlite3_prepare_v2(credentialsDB, \"INSERT INTO creds(password) VALUES(?)\", -1, &stmt, NULL) == SQLITE_OK) {\n sqlite3_bind_text(stmt, 1, password, -1, SQLITE_TRANSIENT);\n sqlite3_step(stmt);\n sqlite3_finalize(stmt);\n sqlite3_close(credentialsDB);\n }\n }\n}\n\n\n```\n\n## References\n* M. Dowd, J. McDonald and J. Schuhm, *The Art of Software Security Assessment*, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.\n* M. Howard and D. LeBlanc, *Writing Secure Code*, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.\n* Common Weakness Enumeration: [CWE-313](https://cwe.mitre.org/data/definitions/313.html).\n", + "markdown" : "# Cleartext storage of sensitive information in an SQLite database\nSensitive information that is stored in an unencrypted SQLite database is accessible to an attacker who gains access to the database.\n\n\n## Recommendation\nEnsure that if sensitive information is stored in a database then the database is always encrypted.\n\n\n## Example\nThe following example shows two ways of storing information in an SQLite database. In the 'BAD' case, the credentials are simply stored in cleartext. In the 'GOOD' case, the database (and thus the credentials) are encrypted.\n\n\n```c\n\nvoid bad(void) {\n char *password = \"cleartext password\";\n sqlite3 *credentialsDB;\n sqlite3_stmt *stmt;\n\n if (sqlite3_open(\"credentials.db\", &credentialsDB) == SQLITE_OK) {\n // BAD: database opened without encryption being enabled\n sqlite3_exec(credentialsDB, \"CREATE TABLE IF NOT EXISTS creds (password TEXT);\", NULL, NULL, NULL);\n if (sqlite3_prepare_v2(credentialsDB, \"INSERT INTO creds(password) VALUES(?)\", -1, &stmt, NULL) == SQLITE_OK) {\n sqlite3_bind_text(stmt, 1, password, -1, SQLITE_TRANSIENT);\n sqlite3_step(stmt);\n sqlite3_finalize(stmt);\n sqlite3_close(credentialsDB);\n }\n }\n}\n\nvoid good(void) {\n char *password = \"cleartext password\";\n sqlite3 *credentialsDB;\n sqlite3_stmt *stmt;\n\n if (sqlite3_open(\"credentials.db\", &credentialsDB) == SQLITE_OK) {\n // GOOD: database encryption enabled:\n sqlite3_exec(credentialsDB, \"PRAGMA key = 'secretKey!'\", NULL, NULL, NULL);\n sqlite3_exec(credentialsDB, \"CREATE TABLE IF NOT EXISTS creds (password TEXT);\", NULL, NULL, NULL);\n if (sqlite3_prepare_v2(credentialsDB, \"INSERT INTO creds(password) VALUES(?)\", -1, &stmt, NULL) == SQLITE_OK) {\n sqlite3_bind_text(stmt, 1, password, -1, SQLITE_TRANSIENT);\n sqlite3_step(stmt);\n sqlite3_finalize(stmt);\n sqlite3_close(credentialsDB);\n }\n }\n}\n\n\n```\n\n## References\n* M. Dowd, J. McDonald and J. Schuhm, *The Art of Software Security Assessment*, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.\n* M. Howard and D. LeBlanc, *Writing Secure Code*, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.\n* Common Weakness Enumeration: [CWE-313](https://cwe.mitre.org/data/definitions/313.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-313" ], + "description" : "Storing sensitive information in a non-encrypted\n database can expose it to an attacker.", + "id" : "cpp/cleartext-storage-database", + "kind" : "path-problem", + "name" : "Cleartext storage of sensitive information in an SQLite database", + "precision" : "medium", + "problem.severity" : "warning", + "security-severity" : "7.5" + } + }, { + "id" : "cpp/world-writable-file-creation", + "name" : "cpp/world-writable-file-creation", + "shortDescription" : { + "text" : "File created without restricting permissions" + }, + "fullDescription" : { + "text" : "Creating a file that is world-writable can allow an attacker to write to the file." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# File created without restricting permissions\nWhen you create a file, take care to give it the most restrictive permissions possible. A typical mistake is to create the file with world-writable permissions. This can allow an attacker to write to the file, which can give them unexpected control over the program.\n\n\n## Recommendation\nFiles should usually be created with write permissions only for the current user. If broader permissions are needed, including the users' group should be sufficient. It is very rare that a file needs to be world-writable, and care should be taken not to make assumptions about the contents of any such file.\n\nOn Unix systems, it is possible for the user who runs the program to restrict file creation permissions using `umask`. However, a program should not assume that the user will set an `umask`, and should still set restrictive permissions by default.\n\n\n## Example\nThis example shows two ways of writing a default configuration file. Software often does this to provide the user with a convenient starting point for defining their own configuration. However, configuration files can also control important aspects of the software's behavior, so it is important that they cannot be controlled by an attacker.\n\nThe first example creates the default configuration file with the usual \"default\" Unix permissions, `0666`. This makes the file world-writable, so that an attacker could write in their own configuration that would be read by the program. The second example uses more restrictive permissions: a combination of the standard Unix constants `S_IWUSR` and `S_IRUSR` which means that only the current user will have read and write access to the file.\n\n\n```c\nvoid write_default_config_bad() {\n\t// BAD - this is world-writable so any user can overwrite the config\n\tint out = creat(OUTFILE, 0666);\n\tdprintf(out, DEFAULT_CONFIG);\n}\n\nvoid write_default_config_good() {\n\t// GOOD - this allows only the current user to modify the file\n\tint out = creat(OUTFILE, S_IWUSR | S_IRUSR);\n\tdprintf(out, DEFAULT_CONFIG);\n}\n\n```\n\n## References\n* The CERT Oracle Secure Coding Standard for C: [ FIO06-C. Create files with appropriate access permissions ](https://www.securecoding.cert.org/confluence/display/c/FIO06-C.+Create+files+with+appropriate+access+permissions).\n* Common Weakness Enumeration: [CWE-732](https://cwe.mitre.org/data/definitions/732.html).\n", + "markdown" : "# File created without restricting permissions\nWhen you create a file, take care to give it the most restrictive permissions possible. A typical mistake is to create the file with world-writable permissions. This can allow an attacker to write to the file, which can give them unexpected control over the program.\n\n\n## Recommendation\nFiles should usually be created with write permissions only for the current user. If broader permissions are needed, including the users' group should be sufficient. It is very rare that a file needs to be world-writable, and care should be taken not to make assumptions about the contents of any such file.\n\nOn Unix systems, it is possible for the user who runs the program to restrict file creation permissions using `umask`. However, a program should not assume that the user will set an `umask`, and should still set restrictive permissions by default.\n\n\n## Example\nThis example shows two ways of writing a default configuration file. Software often does this to provide the user with a convenient starting point for defining their own configuration. However, configuration files can also control important aspects of the software's behavior, so it is important that they cannot be controlled by an attacker.\n\nThe first example creates the default configuration file with the usual \"default\" Unix permissions, `0666`. This makes the file world-writable, so that an attacker could write in their own configuration that would be read by the program. The second example uses more restrictive permissions: a combination of the standard Unix constants `S_IWUSR` and `S_IRUSR` which means that only the current user will have read and write access to the file.\n\n\n```c\nvoid write_default_config_bad() {\n\t// BAD - this is world-writable so any user can overwrite the config\n\tint out = creat(OUTFILE, 0666);\n\tdprintf(out, DEFAULT_CONFIG);\n}\n\nvoid write_default_config_good() {\n\t// GOOD - this allows only the current user to modify the file\n\tint out = creat(OUTFILE, S_IWUSR | S_IRUSR);\n\tdprintf(out, DEFAULT_CONFIG);\n}\n\n```\n\n## References\n* The CERT Oracle Secure Coding Standard for C: [ FIO06-C. Create files with appropriate access permissions ](https://www.securecoding.cert.org/confluence/display/c/FIO06-C.+Create+files+with+appropriate+access+permissions).\n* Common Weakness Enumeration: [CWE-732](https://cwe.mitre.org/data/definitions/732.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-732" ], + "description" : "Creating a file that is world-writable can allow an attacker to write to the file.", + "id" : "cpp/world-writable-file-creation", + "kind" : "problem", + "name" : "File created without restricting permissions", + "precision" : "medium", + "problem.severity" : "warning", + "security-severity" : "7.8" + } + }, { + "id" : "cpp/unterminated-variadic-call", + "name" : "cpp/unterminated-variadic-call", + "shortDescription" : { + "text" : "Unterminated variadic call" + }, + "fullDescription" : { + "text" : "Calling a variadic function without a sentinel value may result in a buffer overflow if the function expects a specific value to terminate the argument list." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Unterminated variadic call\nThe program calls a function that expects the variable argument list to be terminated with a sentinel value (typically NULL, 0 or -1). In this case, the sentinel value has been omitted as a final argument. This defect may result in incorrect behavior of the function and unintended stack memory access, leading to incorrect program results, instability, and even vulnerability to buffer overflow style attacks.\n\n\n## Recommendation\nEach description of a defect highlighted by this rule includes a suggested value for the terminator. Check that this value is correct, then add it to the end of the call.\n\n\n## Example\n\n```cpp\n#include \n\nvoid pushStrings(char *firstString, ...)\n{\n\tva_list args;\n\tchar *arg;\n\n\tva_start(args, firstString);\n\n\t// process inputs, beginning with firstString, ending when NULL is reached\n\targ = firstString;\n\twhile (arg != NULL)\n\t{\n\t\t// push the string\n\t\tpushString(arg);\n\t\n\t\t// move on to the next input\n\t\targ = va_arg(args, char *);\n\t}\n\n\tva_end(args);\n}\n\nvoid badFunction()\n{\n\tpushStrings(\"hello\", \"world\", NULL); // OK\n\t\n\tpushStrings(\"apple\", \"pear\", \"banana\", NULL); // OK\n\n\tpushStrings(\"car\", \"bus\", \"train\"); // BAD, not terminated with the expected NULL\n}\n```\nIn this example, the third call to `pushStrings` is not correctly terminated. This call should be updated to include `NULL` as the fourth and final argument to this call.\n\n\n## References\n* Common Weakness Enumeration: [CWE-121](https://cwe.mitre.org/data/definitions/121.html).\n", + "markdown" : "# Unterminated variadic call\nThe program calls a function that expects the variable argument list to be terminated with a sentinel value (typically NULL, 0 or -1). In this case, the sentinel value has been omitted as a final argument. This defect may result in incorrect behavior of the function and unintended stack memory access, leading to incorrect program results, instability, and even vulnerability to buffer overflow style attacks.\n\n\n## Recommendation\nEach description of a defect highlighted by this rule includes a suggested value for the terminator. Check that this value is correct, then add it to the end of the call.\n\n\n## Example\n\n```cpp\n#include \n\nvoid pushStrings(char *firstString, ...)\n{\n\tva_list args;\n\tchar *arg;\n\n\tva_start(args, firstString);\n\n\t// process inputs, beginning with firstString, ending when NULL is reached\n\targ = firstString;\n\twhile (arg != NULL)\n\t{\n\t\t// push the string\n\t\tpushString(arg);\n\t\n\t\t// move on to the next input\n\t\targ = va_arg(args, char *);\n\t}\n\n\tva_end(args);\n}\n\nvoid badFunction()\n{\n\tpushStrings(\"hello\", \"world\", NULL); // OK\n\t\n\tpushStrings(\"apple\", \"pear\", \"banana\", NULL); // OK\n\n\tpushStrings(\"car\", \"bus\", \"train\"); // BAD, not terminated with the expected NULL\n}\n```\nIn this example, the third call to `pushStrings` is not correctly terminated. This call should be updated to include `NULL` as the fourth and final argument to this call.\n\n\n## References\n* Common Weakness Enumeration: [CWE-121](https://cwe.mitre.org/data/definitions/121.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "security", "external/cwe/cwe-121" ], + "description" : "Calling a variadic function without a sentinel value\n may result in a buffer overflow if the function expects\n a specific value to terminate the argument list.", + "id" : "cpp/unterminated-variadic-call", + "kind" : "problem", + "name" : "Unterminated variadic call", + "precision" : "medium", + "problem.severity" : "warning", + "security-severity" : "8.8" + } + }, { + "id" : "cpp/potentially-dangerous-function", + "name" : "cpp/potentially-dangerous-function", + "shortDescription" : { + "text" : "Use of potentially dangerous function" + }, + "fullDescription" : { + "text" : "Use of a standard library function that is not thread-safe." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Use of potentially dangerous function\nThis rule finds calls to functions that are dangerous to use. Currently, it checks for calls to `gmtime`, `localtime`, `ctime` and `asctime`.\n\nThe time related functions such as `gmtime` fill data into a `tm` struct or `char` array in shared memory and then returns a pointer to that memory. If the function is called from multiple places in the same program, and especially if it is called from multiple threads in the same program, then the calls will overwrite each other's data.\n\n\n## Recommendation\nReplace calls to `gmtime` with `gmtime_r`. With `gmtime_r`, the application code manages allocation of the `tm` struct. That way, separate calls to the function can use their own storage.\n\nSimilarly replace calls to `localtime` with `localtime_r`, calls to `ctime` with `ctime_r` and calls to `asctime` with `asctime_r` (if those functions exist on your platform).\n\n\n## Example\nThe following example checks the local time in two ways:\n\n\n```c\n// BAD: using gmtime\nint is_morning_bad() {\n const time_t now_seconds = time(NULL);\n struct tm *now = gmtime(&now_seconds);\n return (now->tm_hour < 12);\n}\n\n// GOOD: using gmtime_r\nint is_morning_good() {\n const time_t now_seconds = time(NULL);\n struct tm now;\n gmtime_r(&now_seconds, &now);\n return (now.tm_hour < 12);\n}\n\n```\nThe first version uses `gmtime`, so it is vulnerable to its data being overwritten by another thread. Even if this code is not used in a multi-threaded context right now, future changes may make the program multi-threaded. The second version of the code uses `gmtime_r`. Since it allocates a new `tm` struct on every call, it is immune to other calls to `gmtime` or `gmtime_r`.\n\n\n## References\n* SEI CERT C Coding Standard: [CON33-C. Avoid race conditions when using library functions](https://wiki.sei.cmu.edu/confluence/display/c/CON33-C.+Avoid+race+conditions+when+using+library+functions).\n* Common Weakness Enumeration: [CWE-676](https://cwe.mitre.org/data/definitions/676.html).\n", + "markdown" : "# Use of potentially dangerous function\nThis rule finds calls to functions that are dangerous to use. Currently, it checks for calls to `gmtime`, `localtime`, `ctime` and `asctime`.\n\nThe time related functions such as `gmtime` fill data into a `tm` struct or `char` array in shared memory and then returns a pointer to that memory. If the function is called from multiple places in the same program, and especially if it is called from multiple threads in the same program, then the calls will overwrite each other's data.\n\n\n## Recommendation\nReplace calls to `gmtime` with `gmtime_r`. With `gmtime_r`, the application code manages allocation of the `tm` struct. That way, separate calls to the function can use their own storage.\n\nSimilarly replace calls to `localtime` with `localtime_r`, calls to `ctime` with `ctime_r` and calls to `asctime` with `asctime_r` (if those functions exist on your platform).\n\n\n## Example\nThe following example checks the local time in two ways:\n\n\n```c\n// BAD: using gmtime\nint is_morning_bad() {\n const time_t now_seconds = time(NULL);\n struct tm *now = gmtime(&now_seconds);\n return (now->tm_hour < 12);\n}\n\n// GOOD: using gmtime_r\nint is_morning_good() {\n const time_t now_seconds = time(NULL);\n struct tm now;\n gmtime_r(&now_seconds, &now);\n return (now.tm_hour < 12);\n}\n\n```\nThe first version uses `gmtime`, so it is vulnerable to its data being overwritten by another thread. Even if this code is not used in a multi-threaded context right now, future changes may make the program multi-threaded. The second version of the code uses `gmtime_r`. Since it allocates a new `tm` struct on every call, it is immune to other calls to `gmtime` or `gmtime_r`.\n\n\n## References\n* SEI CERT C Coding Standard: [CON33-C. Avoid race conditions when using library functions](https://wiki.sei.cmu.edu/confluence/display/c/CON33-C.+Avoid+race+conditions+when+using+library+functions).\n* Common Weakness Enumeration: [CWE-676](https://cwe.mitre.org/data/definitions/676.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "security", "external/cwe/cwe-676" ], + "description" : "Use of a standard library function that is not thread-safe.", + "id" : "cpp/potentially-dangerous-function", + "kind" : "problem", + "name" : "Use of potentially dangerous function", + "precision" : "medium", + "problem.severity" : "warning", + "security-severity" : "10.0" + } + }, { + "id" : "cpp/suspicious-pointer-scaling", + "name" : "cpp/suspicious-pointer-scaling", + "shortDescription" : { + "text" : "Suspicious pointer scaling" + }, + "fullDescription" : { + "text" : "Implicit scaling of pointer arithmetic expressions can cause buffer overflow conditions." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Suspicious pointer scaling\nPointer arithmetic in C and C++ is automatically scaled according to the size of the data type. For example, if the type of `p` is `T*` and `sizeof(T) == 4` then the expression `p+1` adds 4 bytes to `p`. This can cause a buffer overflow condition if the programmer forgets that they are adding a multiple of `sizeof(T)`, rather than a number of bytes.\n\nThis query finds pointer arithmetic expressions where it appears likely that the programmer has forgotten that the offset is automatically scaled.\n\n\n## Recommendation\n1. Whenever possible, use the array subscript operator rather than pointer arithmetic. For example, replace `*(p+k)` with `p[k]`.\n1. Cast to the correct type before using pointer arithmetic. For example, if the type of `p` is `int*` but it really points to an array of type `double[]` then use the syntax `(double*)p + k` to get a pointer to the `k`'th element of the array.\n\n## Example\n\n```cpp\nint example1(int i) {\n int intArray[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };\n int *intPointer = intArray;\n // BAD: the offset is already automatically scaled by sizeof(int),\n // so this code will compute the wrong offset.\n return *(intPointer + (i * sizeof(int)));\n}\n\nint example2(int i) {\n int intArray[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };\n int *intPointer = intArray;\n // GOOD: the offset is automatically scaled by sizeof(int).\n return *(intPointer + i);\n}\n\n```\n\n## References\n* Common Weakness Enumeration: [CWE-468](https://cwe.mitre.org/data/definitions/468.html).\n", + "markdown" : "# Suspicious pointer scaling\nPointer arithmetic in C and C++ is automatically scaled according to the size of the data type. For example, if the type of `p` is `T*` and `sizeof(T) == 4` then the expression `p+1` adds 4 bytes to `p`. This can cause a buffer overflow condition if the programmer forgets that they are adding a multiple of `sizeof(T)`, rather than a number of bytes.\n\nThis query finds pointer arithmetic expressions where it appears likely that the programmer has forgotten that the offset is automatically scaled.\n\n\n## Recommendation\n1. Whenever possible, use the array subscript operator rather than pointer arithmetic. For example, replace `*(p+k)` with `p[k]`.\n1. Cast to the correct type before using pointer arithmetic. For example, if the type of `p` is `int*` but it really points to an array of type `double[]` then use the syntax `(double*)p + k` to get a pointer to the `k`'th element of the array.\n\n## Example\n\n```cpp\nint example1(int i) {\n int intArray[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };\n int *intPointer = intArray;\n // BAD: the offset is already automatically scaled by sizeof(int),\n // so this code will compute the wrong offset.\n return *(intPointer + (i * sizeof(int)));\n}\n\nint example2(int i) {\n int intArray[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };\n int *intPointer = intArray;\n // GOOD: the offset is automatically scaled by sizeof(int).\n return *(intPointer + i);\n}\n\n```\n\n## References\n* Common Weakness Enumeration: [CWE-468](https://cwe.mitre.org/data/definitions/468.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-468" ], + "description" : "Implicit scaling of pointer arithmetic expressions\n can cause buffer overflow conditions.", + "id" : "cpp/suspicious-pointer-scaling", + "kind" : "problem", + "name" : "Suspicious pointer scaling", + "precision" : "medium", + "problem.severity" : "warning", + "security-severity" : "8.8" + } + }, { + "id" : "cpp/suspicious-pointer-scaling-void", + "name" : "cpp/suspicious-pointer-scaling-void", + "shortDescription" : { + "text" : "Suspicious pointer scaling to void" + }, + "fullDescription" : { + "text" : "Implicit scaling of pointer arithmetic expressions can cause buffer overflow conditions." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Suspicious pointer scaling to void\nCasting arbitrary pointers into `void*` and then accessing their contents should be done with care. The results may not be portable.\n\nThis query finds pointer arithmetic expressions where a pointer to `void` (or similar) is then cast to another type and dereferenced.\n\n\n## Recommendation\n1. Whenever possible, use the array subscript operator rather than pointer arithmetic. For example, replace `*(p+k)` with `p[k]`.\n1. Cast to the correct type before using pointer arithmetic. For example, if the type of `p` is `void*` but it really points to an array of type `double[]` then use the syntax `(double*)p + k` to get a pointer to the `k`'th element of the array.\n1. If pointer arithmetic must be done with a single-byte width, prefer `char *` to `void *`, as pointer arithmetic on `void *` is a nonstandard GNU extension.\n\n## Example\n\n```cpp\nchar example1(int i) {\n int intArray[5] = { 1, 2, 3, 4, 5 };\n void *voidPointer = (void *)intArray;\n // BAD: the pointer arithmetic uses type void*, so the offset\n // is not scaled by sizeof(int).\n return *(voidPointer + i);\n}\n\nint example2(int i) {\n int intArray[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };\n int *intPointer = intArray;\n // GOOD: the offset is automatically scaled by sizeof(int).\n return *(intPointer + i);\n}\n\n```\n\n## References\n* Common Weakness Enumeration: [CWE-468](https://cwe.mitre.org/data/definitions/468.html).\n", + "markdown" : "# Suspicious pointer scaling to void\nCasting arbitrary pointers into `void*` and then accessing their contents should be done with care. The results may not be portable.\n\nThis query finds pointer arithmetic expressions where a pointer to `void` (or similar) is then cast to another type and dereferenced.\n\n\n## Recommendation\n1. Whenever possible, use the array subscript operator rather than pointer arithmetic. For example, replace `*(p+k)` with `p[k]`.\n1. Cast to the correct type before using pointer arithmetic. For example, if the type of `p` is `void*` but it really points to an array of type `double[]` then use the syntax `(double*)p + k` to get a pointer to the `k`'th element of the array.\n1. If pointer arithmetic must be done with a single-byte width, prefer `char *` to `void *`, as pointer arithmetic on `void *` is a nonstandard GNU extension.\n\n## Example\n\n```cpp\nchar example1(int i) {\n int intArray[5] = { 1, 2, 3, 4, 5 };\n void *voidPointer = (void *)intArray;\n // BAD: the pointer arithmetic uses type void*, so the offset\n // is not scaled by sizeof(int).\n return *(voidPointer + i);\n}\n\nint example2(int i) {\n int intArray[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };\n int *intPointer = intArray;\n // GOOD: the offset is automatically scaled by sizeof(int).\n return *(intPointer + i);\n}\n\n```\n\n## References\n* Common Weakness Enumeration: [CWE-468](https://cwe.mitre.org/data/definitions/468.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-468" ], + "description" : "Implicit scaling of pointer arithmetic expressions\n can cause buffer overflow conditions.", + "id" : "cpp/suspicious-pointer-scaling-void", + "kind" : "problem", + "name" : "Suspicious pointer scaling to void", + "precision" : "medium", + "problem.severity" : "warning", + "security-severity" : "8.8" + } + }, { + "id" : "cpp/unsigned-difference-expression-compared-zero", + "name" : "cpp/unsigned-difference-expression-compared-zero", + "shortDescription" : { + "text" : "Unsigned difference expression compared to zero" + }, + "fullDescription" : { + "text" : "A subtraction with an unsigned result can never be negative. Using such an expression in a relational comparison with `0` is likely to be wrong." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Unsigned difference expression compared to zero\nThis rule finds relational comparisons between the result of an unsigned subtraction and the value `0`. Such comparisons are likely to be wrong as the value of an unsigned subtraction can never be negative. So the relational comparison ends up checking whether the result of the subtraction is equal to `0`. This is probably not what the programmer intended.\n\n\n## Recommendation\nIf a relational comparison is intended, consider casting the result of the subtraction to a signed type. If the intention was to test for equality, consider replacing the relational comparison with an equality test.\n\n\n## Example\n\n```c\nunsigned limit = get_limit();\nunsigned total = 0;\nwhile (limit - total > 0) { // wrong: if `total` is greater than `limit` this will underflow and continue executing the loop.\n total += get_data();\n}\n```\n\n## References\n* SEI CERT C Coding Standard: [INT02-C. Understand integer conversion rules](https://wiki.sei.cmu.edu/confluence/display/c/INT02-C.+Understand+integer+conversion+rules).\n* Common Weakness Enumeration: [CWE-191](https://cwe.mitre.org/data/definitions/191.html).\n", + "markdown" : "# Unsigned difference expression compared to zero\nThis rule finds relational comparisons between the result of an unsigned subtraction and the value `0`. Such comparisons are likely to be wrong as the value of an unsigned subtraction can never be negative. So the relational comparison ends up checking whether the result of the subtraction is equal to `0`. This is probably not what the programmer intended.\n\n\n## Recommendation\nIf a relational comparison is intended, consider casting the result of the subtraction to a signed type. If the intention was to test for equality, consider replacing the relational comparison with an equality test.\n\n\n## Example\n\n```c\nunsigned limit = get_limit();\nunsigned total = 0;\nwhile (limit - total > 0) { // wrong: if `total` is greater than `limit` this will underflow and continue executing the loop.\n total += get_data();\n}\n```\n\n## References\n* SEI CERT C Coding Standard: [INT02-C. Understand integer conversion rules](https://wiki.sei.cmu.edu/confluence/display/c/INT02-C.+Understand+integer+conversion+rules).\n* Common Weakness Enumeration: [CWE-191](https://cwe.mitre.org/data/definitions/191.html).\n" + }, + "properties" : { + "tags" : [ "security", "correctness", "external/cwe/cwe-191" ], + "description" : "A subtraction with an unsigned result can never be negative. Using such an expression in a relational comparison with `0` is likely to be wrong.", + "id" : "cpp/unsigned-difference-expression-compared-zero", + "kind" : "problem", + "name" : "Unsigned difference expression compared to zero", + "precision" : "medium", + "problem.severity" : "warning", + "security-severity" : "9.8" + } + }, { + "id" : "cpp/invalid-pointer-deref", + "name" : "cpp/invalid-pointer-deref", + "shortDescription" : { + "text" : "Invalid pointer dereference" + }, + "fullDescription" : { + "text" : "Dereferencing an out-of-bounds pointer is undefined behavior and may lead to security vulnerabilities." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Invalid pointer dereference\nThe program performs an out-of-bounds read or write operation, which can cause program instability. In addition, attackers may take advantage of the situation, and implement techniques to use this vulnerability to execute arbitrary code.\n\n\n## Recommendation\nEnsure that pointer dereferences are properly guarded to ensure that they cannot be used to read or write past the end of the allocation.\n\n\n## Example\nThe first example allocates a buffer of size `size` and creates a local variable that stores the location that is one byte past the end of the allocation. This local variable is then dereferenced, which results in an out-of-bounds write. The second example subtracts one from the `end` variable before dereferencing it. This subtraction ensures that the write correctly updates the final byte of the allocation.\n\n\n```cpp\nvoid *malloc(unsigned);\nunsigned get_size();\nvoid write_data(const unsigned char*, const unsigned char*);\n\nint main(int argc, char* argv[]) {\n unsigned size = get_size();\n \n {\n unsigned char *begin = (unsigned char*)malloc(size);\n if(!begin) return -1;\n\n unsigned char* end = begin + size;\n write_data(begin, end);\n *end = '\\0'; // BAD: Out-of-bounds write\n }\n\n {\n unsigned char *begin = (unsigned char*)malloc(size);\n if(!begin) return -1;\n\n unsigned char* end = begin + size;\n write_data(begin, end);\n *(end - 1) = '\\0'; // GOOD: writing to the last byte\n }\n\n}\n```\n\n## References\n* CERT C Coding Standard: [ARR30-C. Do not form or use out-of-bounds pointers or array subscripts](https://wiki.sei.cmu.edu/confluence/display/c/ARR30-C.+Do+not+form+or+use+out-of-bounds+pointers+or+array+subscripts).\n* OWASP: [Buffer Overflow](https://owasp.org/www-community/vulnerabilities/Buffer_Overflow).\n* Common Weakness Enumeration: [CWE-119](https://cwe.mitre.org/data/definitions/119.html).\n* Common Weakness Enumeration: [CWE-125](https://cwe.mitre.org/data/definitions/125.html).\n* Common Weakness Enumeration: [CWE-193](https://cwe.mitre.org/data/definitions/193.html).\n* Common Weakness Enumeration: [CWE-787](https://cwe.mitre.org/data/definitions/787.html).\n", + "markdown" : "# Invalid pointer dereference\nThe program performs an out-of-bounds read or write operation, which can cause program instability. In addition, attackers may take advantage of the situation, and implement techniques to use this vulnerability to execute arbitrary code.\n\n\n## Recommendation\nEnsure that pointer dereferences are properly guarded to ensure that they cannot be used to read or write past the end of the allocation.\n\n\n## Example\nThe first example allocates a buffer of size `size` and creates a local variable that stores the location that is one byte past the end of the allocation. This local variable is then dereferenced, which results in an out-of-bounds write. The second example subtracts one from the `end` variable before dereferencing it. This subtraction ensures that the write correctly updates the final byte of the allocation.\n\n\n```cpp\nvoid *malloc(unsigned);\nunsigned get_size();\nvoid write_data(const unsigned char*, const unsigned char*);\n\nint main(int argc, char* argv[]) {\n unsigned size = get_size();\n \n {\n unsigned char *begin = (unsigned char*)malloc(size);\n if(!begin) return -1;\n\n unsigned char* end = begin + size;\n write_data(begin, end);\n *end = '\\0'; // BAD: Out-of-bounds write\n }\n\n {\n unsigned char *begin = (unsigned char*)malloc(size);\n if(!begin) return -1;\n\n unsigned char* end = begin + size;\n write_data(begin, end);\n *(end - 1) = '\\0'; // GOOD: writing to the last byte\n }\n\n}\n```\n\n## References\n* CERT C Coding Standard: [ARR30-C. Do not form or use out-of-bounds pointers or array subscripts](https://wiki.sei.cmu.edu/confluence/display/c/ARR30-C.+Do+not+form+or+use+out-of-bounds+pointers+or+array+subscripts).\n* OWASP: [Buffer Overflow](https://owasp.org/www-community/vulnerabilities/Buffer_Overflow).\n* Common Weakness Enumeration: [CWE-119](https://cwe.mitre.org/data/definitions/119.html).\n* Common Weakness Enumeration: [CWE-125](https://cwe.mitre.org/data/definitions/125.html).\n* Common Weakness Enumeration: [CWE-193](https://cwe.mitre.org/data/definitions/193.html).\n* Common Weakness Enumeration: [CWE-787](https://cwe.mitre.org/data/definitions/787.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "security", "external/cwe/cwe-119", "external/cwe/cwe-125", "external/cwe/cwe-193", "external/cwe/cwe-787" ], + "description" : "Dereferencing an out-of-bounds pointer is undefined behavior and may lead to security vulnerabilities.", + "id" : "cpp/invalid-pointer-deref", + "kind" : "path-problem", + "name" : "Invalid pointer dereference", + "precision" : "medium", + "problem.severity" : "error", + "security-severity" : "9.3" + } + }, { + "id" : "cpp/overrun-write", + "name" : "cpp/overrun-write", + "shortDescription" : { + "text" : "Overrunning write" + }, + "fullDescription" : { + "text" : "Exceeding the size of a static array during write or access operations may result in a buffer overflow." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Overrunning write\nYou must ensure that you do not exceed the size of an allocation during write and read operations. If an operation attempts to write to or access an element that is outside the range of the allocation then this results in a buffer overflow. Buffer overflows can lead to anything from a segmentation fault to a security vulnerability.\n\n\n## Recommendation\nCheck the offsets and sizes used in the highlighted operations to ensure that a buffer overflow will not occur.\n\n\n## Example\n\n```cpp\nint f(char * s, unsigned size) {\n\tchar* buf = (char*)malloc(size);\n\n\tstrncpy(buf, s, size + 1); // wrong: copy may exceed size of buf\n\n\tfor (int i = 0; i <= size; i++) { // wrong: upper limit that is higher than size of buf\n\t\tcout << buf[i];\n\t}\n}\n\n```\n\n## References\n* I. Gerg. *An Overview and Example of the Buffer-Overflow Exploit*. IANewsletter vol 7 no 4. 2005.\n* M. Donaldson. *Inside the Buffer Overflow Attack: Mechanism, Method & Prevention*. SANS Institute InfoSec Reading Room. 2002.\n* Common Weakness Enumeration: [CWE-119](https://cwe.mitre.org/data/definitions/119.html).\n* Common Weakness Enumeration: [CWE-131](https://cwe.mitre.org/data/definitions/131.html).\n", + "markdown" : "# Overrunning write\nYou must ensure that you do not exceed the size of an allocation during write and read operations. If an operation attempts to write to or access an element that is outside the range of the allocation then this results in a buffer overflow. Buffer overflows can lead to anything from a segmentation fault to a security vulnerability.\n\n\n## Recommendation\nCheck the offsets and sizes used in the highlighted operations to ensure that a buffer overflow will not occur.\n\n\n## Example\n\n```cpp\nint f(char * s, unsigned size) {\n\tchar* buf = (char*)malloc(size);\n\n\tstrncpy(buf, s, size + 1); // wrong: copy may exceed size of buf\n\n\tfor (int i = 0; i <= size; i++) { // wrong: upper limit that is higher than size of buf\n\t\tcout << buf[i];\n\t}\n}\n\n```\n\n## References\n* I. Gerg. *An Overview and Example of the Buffer-Overflow Exploit*. IANewsletter vol 7 no 4. 2005.\n* M. Donaldson. *Inside the Buffer Overflow Attack: Mechanism, Method & Prevention*. SANS Institute InfoSec Reading Room. 2002.\n* Common Weakness Enumeration: [CWE-119](https://cwe.mitre.org/data/definitions/119.html).\n* Common Weakness Enumeration: [CWE-131](https://cwe.mitre.org/data/definitions/131.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "security", "external/cwe/cwe-119", "external/cwe/cwe-131" ], + "description" : "Exceeding the size of a static array during write or access operations\n may result in a buffer overflow.", + "id" : "cpp/overrun-write", + "kind" : "path-problem", + "name" : "Overrunning write", + "precision" : "medium", + "problem.severity" : "error", + "security-severity" : "9.3" + } + }, { + "id" : "cpp/tainted-permissions-check", + "name" : "cpp/tainted-permissions-check", + "shortDescription" : { + "text" : "Untrusted input for a condition" + }, + "fullDescription" : { + "text" : "Using untrusted inputs in a statement that makes a security decision makes code vulnerable to attack." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Untrusted input for a condition\nThis rule finds code where untrusted inputs are used in an `if` statement, and the body of that statement makes a security decision. This is an example of CWE-807 and makes the program vulnerable to attack. An attacker might be able to gain unauthorized access to the system by manipulating external inputs to the system.\n\n\n## Recommendation\nIn most cases, you need to add or strengthen the checks made on the user-supplied data to ensure its integrity. The user-supplied data can then be used as a trusted input to the security decision. For example, instead of checking an HTTP cookie against a predictable fixed string, check a cookie against a randomly generated session key.\n\nThis rule may highlight a few conditions where user-supplied data has been checked and can be trusted. It is not always possible to determine if the checks applied to data are enough to ensure security.\n\n\n## Example\nThe following example is included in CWE 807.\n\n\n```c\nstruct hostent *hp;struct in_addr myaddr;\nchar* tHost = \"trustme.example.com\";\nmyaddr.s_addr=inet_addr(ip_addr_string);\n\nhp = gethostbyaddr((char *) &myaddr, sizeof(struct in_addr), AF_INET);\nif (hp && !strncmp(hp->h_name, tHost, sizeof(tHost))) {\n trusted = true;\n} else {\n trusted = false;\n}\n\n```\nIn this example, the result of a reverse DNS query is compared against a fixed string. An attacker can return an incorrect reverse DNS entry for the requesting IP and thus gain the same access as a legitimate user from `trustme.example.com`.\n\nTo fix the problem in this example, you need to add an additional mechanism to test the user-supplied data. For example, numeric IP addresses could be used.\n\n\n## References\n* Common Weakness Enumeration: [CWE-807](https://cwe.mitre.org/data/definitions/807.html).\n", + "markdown" : "# Untrusted input for a condition\nThis rule finds code where untrusted inputs are used in an `if` statement, and the body of that statement makes a security decision. This is an example of CWE-807 and makes the program vulnerable to attack. An attacker might be able to gain unauthorized access to the system by manipulating external inputs to the system.\n\n\n## Recommendation\nIn most cases, you need to add or strengthen the checks made on the user-supplied data to ensure its integrity. The user-supplied data can then be used as a trusted input to the security decision. For example, instead of checking an HTTP cookie against a predictable fixed string, check a cookie against a randomly generated session key.\n\nThis rule may highlight a few conditions where user-supplied data has been checked and can be trusted. It is not always possible to determine if the checks applied to data are enough to ensure security.\n\n\n## Example\nThe following example is included in CWE 807.\n\n\n```c\nstruct hostent *hp;struct in_addr myaddr;\nchar* tHost = \"trustme.example.com\";\nmyaddr.s_addr=inet_addr(ip_addr_string);\n\nhp = gethostbyaddr((char *) &myaddr, sizeof(struct in_addr), AF_INET);\nif (hp && !strncmp(hp->h_name, tHost, sizeof(tHost))) {\n trusted = true;\n} else {\n trusted = false;\n}\n\n```\nIn this example, the result of a reverse DNS query is compared against a fixed string. An attacker can return an incorrect reverse DNS entry for the requesting IP and thus gain the same access as a legitimate user from `trustme.example.com`.\n\nTo fix the problem in this example, you need to add an additional mechanism to test the user-supplied data. For example, numeric IP addresses could be used.\n\n\n## References\n* Common Weakness Enumeration: [CWE-807](https://cwe.mitre.org/data/definitions/807.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-807" ], + "description" : "Using untrusted inputs in a statement that makes a\n security decision makes code vulnerable to\n attack.", + "id" : "cpp/tainted-permissions-check", + "kind" : "path-problem", + "name" : "Untrusted input for a condition", + "precision" : "medium", + "problem.severity" : "warning", + "security-severity" : "7.5" + } + }, { + "id" : "cpp/certificate-result-conflation", + "name" : "cpp/certificate-result-conflation", + "shortDescription" : { + "text" : "Certificate result conflation" + }, + "fullDescription" : { + "text" : "Only accept SSL certificates that pass certificate verification." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Certificate result conflation\nWhen checking the result of SSL certificate verification, accepting any error code may allow an attacker to impersonate someone who is trusted.\n\n\n## Recommendation\nWhen checking an SSL certificate with `SSL_get_verify_result`, only `X509_V_OK` is a success code. If there is any other result the certificate should not be accepted.\n\n\n## Example\nIn this example the error code `X509_V_ERR_CERT_HAS_EXPIRED` is treated the same as an OK result. An expired certificate should not be accepted as it is more likely to be compromised than a valid certificate.\n\n\n```cpp\n// ...\n\nif (cert = SSL_get_peer_certificate(ssl))\n{\n\tresult = SSL_get_verify_result(ssl);\n\n\tif ((result == X509_V_OK) || (result == X509_V_ERR_CERT_HAS_EXPIRED)) // BAD (conflates OK and a non-OK codes)\n\t{\n\t\tdo_ok();\n\t} else {\n\t\tdo_error();\n\t}\n}\n\n```\nIn the corrected example, only a result of `X509_V_OK` is accepted.\n\n\n```cpp\n// ...\n\nif (cert = SSL_get_peer_certificate(ssl))\n{\n\tresult = SSL_get_verify_result(ssl);\n\n\tif (result == X509_V_OK) // GOOD\n\t{\n\t\tdo_ok();\n\t} else {\n\t\tdo_error();\n\t}\n}\n\n```\n\n## References\n* Common Weakness Enumeration: [CWE-295](https://cwe.mitre.org/data/definitions/295.html).\n", + "markdown" : "# Certificate result conflation\nWhen checking the result of SSL certificate verification, accepting any error code may allow an attacker to impersonate someone who is trusted.\n\n\n## Recommendation\nWhen checking an SSL certificate with `SSL_get_verify_result`, only `X509_V_OK` is a success code. If there is any other result the certificate should not be accepted.\n\n\n## Example\nIn this example the error code `X509_V_ERR_CERT_HAS_EXPIRED` is treated the same as an OK result. An expired certificate should not be accepted as it is more likely to be compromised than a valid certificate.\n\n\n```cpp\n// ...\n\nif (cert = SSL_get_peer_certificate(ssl))\n{\n\tresult = SSL_get_verify_result(ssl);\n\n\tif ((result == X509_V_OK) || (result == X509_V_ERR_CERT_HAS_EXPIRED)) // BAD (conflates OK and a non-OK codes)\n\t{\n\t\tdo_ok();\n\t} else {\n\t\tdo_error();\n\t}\n}\n\n```\nIn the corrected example, only a result of `X509_V_OK` is accepted.\n\n\n```cpp\n// ...\n\nif (cert = SSL_get_peer_certificate(ssl))\n{\n\tresult = SSL_get_verify_result(ssl);\n\n\tif (result == X509_V_OK) // GOOD\n\t{\n\t\tdo_ok();\n\t} else {\n\t\tdo_error();\n\t}\n}\n\n```\n\n## References\n* Common Weakness Enumeration: [CWE-295](https://cwe.mitre.org/data/definitions/295.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-295" ], + "description" : "Only accept SSL certificates that pass certificate verification.", + "id" : "cpp/certificate-result-conflation", + "kind" : "problem", + "name" : "Certificate result conflation", + "precision" : "medium", + "problem.severity" : "error", + "security-severity" : "7.5" + } + }, { + "id" : "cpp/certificate-not-checked", + "name" : "cpp/certificate-not-checked", + "shortDescription" : { + "text" : "Certificate not checked" + }, + "fullDescription" : { + "text" : "Always check the result of certificate verification after fetching an SSL certificate." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "error" + }, + "help" : { + "text" : "# Certificate not checked\nAfter fetching an SSL certificate, always check the result of certificate verification.\n\n\n## Recommendation\nAlways check the result of SSL certificate verification. A certificate that has been revoked may indicate that data is coming from an attacker, whereas a certificate that has expired or was self-signed may indicate an increased likelihood that the data is malicious.\n\n\n## Example\nIn this example, the `SSL_get_peer_certificate` function is used to get the certificate of a peer. However it is unsafe to use that information without checking if the certificate is valid.\n\n\n```cpp\n// ...\n\nX509 *cert = SSL_get_peer_certificate(ssl); // BAD (SSL_get_verify_result is never called)\n\n// ...\n```\nIn the corrected example, we use `SSL_get_verify_result` to check that certificate verification was successful.\n\n\n```cpp\n// ...\n\nX509 *cert = SSL_get_peer_certificate(ssl); // GOOD\nif (cert)\n{\n\tresult = SSL_get_verify_result(ssl);\n\tif (result == X509_V_OK)\n\t{\n\t\t// ...\n```\n\n## References\n* Common Weakness Enumeration: [CWE-295](https://cwe.mitre.org/data/definitions/295.html).\n", + "markdown" : "# Certificate not checked\nAfter fetching an SSL certificate, always check the result of certificate verification.\n\n\n## Recommendation\nAlways check the result of SSL certificate verification. A certificate that has been revoked may indicate that data is coming from an attacker, whereas a certificate that has expired or was self-signed may indicate an increased likelihood that the data is malicious.\n\n\n## Example\nIn this example, the `SSL_get_peer_certificate` function is used to get the certificate of a peer. However it is unsafe to use that information without checking if the certificate is valid.\n\n\n```cpp\n// ...\n\nX509 *cert = SSL_get_peer_certificate(ssl); // BAD (SSL_get_verify_result is never called)\n\n// ...\n```\nIn the corrected example, we use `SSL_get_verify_result` to check that certificate verification was successful.\n\n\n```cpp\n// ...\n\nX509 *cert = SSL_get_peer_certificate(ssl); // GOOD\nif (cert)\n{\n\tresult = SSL_get_verify_result(ssl);\n\tif (result == X509_V_OK)\n\t{\n\t\t// ...\n```\n\n## References\n* Common Weakness Enumeration: [CWE-295](https://cwe.mitre.org/data/definitions/295.html).\n" + }, + "properties" : { + "tags" : [ "security", "external/cwe/cwe-295" ], + "description" : "Always check the result of certificate verification after fetching an SSL certificate.", + "id" : "cpp/certificate-not-checked", + "kind" : "problem", + "name" : "Certificate not checked", + "precision" : "medium", + "problem.severity" : "error", + "security-severity" : "7.5" + } + }, { + "id" : "cpp/missing-check-scanf", + "name" : "cpp/missing-check-scanf", + "shortDescription" : { + "text" : "Missing return-value check for a 'scanf'-like function" + }, + "fullDescription" : { + "text" : "Failing to check that a call to 'scanf' actually writes to an output variable can lead to unexpected behavior at reading time." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Missing return-value check for a 'scanf'-like function\nThis query finds calls of `scanf`-like functions with missing or improper return-value checking.\n\nSpecifically, the query flags uses of variables that may have been modified by `scanf` and subsequently are used without being guarded by a correct return-value check. A proper check is one that ensures that the corresponding `scanf` has returned (at least) a certain minimum constant.\n\nFunctions in the `scanf` family return either EOF (a negative value) in case of IO failure, or the number of items successfully read from the input. Consequently, a simple check that the return value is truthy (nonzero) is not enough.\n\n> WARNING: This query has medium precision because, in the current implementation, it takes a strict stance on unguarded uses of output variables, and flags them as problematic even if they have already been initialized.\n\n## Recommendation\nEnsure that all subsequent uses of `scanf` output arguments occur in a branch of an `if` statement (or similar), in which it is known that the corresponding `scanf` call has in fact read all possible items from its input. This can be done by comparing the return value to a numerical constant.\n\n\n## Example\nThis example shows different ways of guarding a `scanf` output:\n\n\n```cpp\n{\n int i, j, r;\n\n r = scanf(\"%d %d\", &i, &j);\n\n use(i); // BAD: i is not guarded\n\n if (r >= 1) {\n use(i); // GOOD: i is guarded correctly\n use(j); // BAD: j is guarded incorrectly\n }\n\n if (r != 2)\n return;\n\n use(j); // GOOD: j is guarded correctly\n}\n\n```\n\n## References\n* SEI CERT C++ Coding Standard: [ERR62-CPP. Detect errors when converting a string to a number](https://wiki.sei.cmu.edu/confluence/display/cplusplus/ERR62-CPP.+Detect+errors+when+converting+a+string+to+a+number).\n* SEI CERT C Coding Standard: [ERR33-C. Detect and handle standard library errors](https://wiki.sei.cmu.edu/confluence/display/c/ERR33-C.+Detect+and+handle+standard+library+errors).\n* cppreference.com: [scanf, fscanf, sscanf, scanf_s, fscanf_s, sscanf_s](https://en.cppreference.com/w/c/io/fscanf).\n* Common Weakness Enumeration: [CWE-252](https://cwe.mitre.org/data/definitions/252.html).\n* Common Weakness Enumeration: [CWE-253](https://cwe.mitre.org/data/definitions/253.html).\n", + "markdown" : "# Missing return-value check for a 'scanf'-like function\nThis query finds calls of `scanf`-like functions with missing or improper return-value checking.\n\nSpecifically, the query flags uses of variables that may have been modified by `scanf` and subsequently are used without being guarded by a correct return-value check. A proper check is one that ensures that the corresponding `scanf` has returned (at least) a certain minimum constant.\n\nFunctions in the `scanf` family return either EOF (a negative value) in case of IO failure, or the number of items successfully read from the input. Consequently, a simple check that the return value is truthy (nonzero) is not enough.\n\n> WARNING: This query has medium precision because, in the current implementation, it takes a strict stance on unguarded uses of output variables, and flags them as problematic even if they have already been initialized.\n\n## Recommendation\nEnsure that all subsequent uses of `scanf` output arguments occur in a branch of an `if` statement (or similar), in which it is known that the corresponding `scanf` call has in fact read all possible items from its input. This can be done by comparing the return value to a numerical constant.\n\n\n## Example\nThis example shows different ways of guarding a `scanf` output:\n\n\n```cpp\n{\n int i, j, r;\n\n r = scanf(\"%d %d\", &i, &j);\n\n use(i); // BAD: i is not guarded\n\n if (r >= 1) {\n use(i); // GOOD: i is guarded correctly\n use(j); // BAD: j is guarded incorrectly\n }\n\n if (r != 2)\n return;\n\n use(j); // GOOD: j is guarded correctly\n}\n\n```\n\n## References\n* SEI CERT C++ Coding Standard: [ERR62-CPP. Detect errors when converting a string to a number](https://wiki.sei.cmu.edu/confluence/display/cplusplus/ERR62-CPP.+Detect+errors+when+converting+a+string+to+a+number).\n* SEI CERT C Coding Standard: [ERR33-C. Detect and handle standard library errors](https://wiki.sei.cmu.edu/confluence/display/c/ERR33-C.+Detect+and+handle+standard+library+errors).\n* cppreference.com: [scanf, fscanf, sscanf, scanf_s, fscanf_s, sscanf_s](https://en.cppreference.com/w/c/io/fscanf).\n* Common Weakness Enumeration: [CWE-252](https://cwe.mitre.org/data/definitions/252.html).\n* Common Weakness Enumeration: [CWE-253](https://cwe.mitre.org/data/definitions/253.html).\n" + }, + "properties" : { + "tags" : [ "security", "correctness", "external/cwe/cwe-252", "external/cwe/cwe-253" ], + "description" : "Failing to check that a call to 'scanf' actually writes to an\n output variable can lead to unexpected behavior at reading time.", + "id" : "cpp/missing-check-scanf", + "kind" : "problem", + "name" : "Missing return-value check for a 'scanf'-like function", + "precision" : "medium", + "problem.severity" : "warning", + "security-severity" : "7.5" + } + }, { + "id" : "cpp/allocation-too-small", + "name" : "cpp/allocation-too-small", + "shortDescription" : { + "text" : "Not enough memory allocated for pointer type" + }, + "fullDescription" : { + "text" : "Calling 'malloc', 'calloc' or 'realloc' without allocating enough memory to contain an instance of the type of the pointer may result in a buffer overflow" + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Not enough memory allocated for pointer type\nWhen you allocate an array from memory using `malloc`, `calloc` or `realloc`, you should ensure that you allocate enough memory to contain an instance of the required pointer type. Calls that are assigned to a non-void pointer variable, but do not allocate enough memory will cause a buffer overflow when a field accessed on the pointer points to memory that is beyond the allocated array. Buffer overflows can lead to anything from a segmentation fault to a security vulnerability.\n\n\n## Recommendation\nThe highlighted call allocates memory that is too small to contain an instance of the type of the pointer, which can cause a memory overrun. Use the `sizeof` operator to ensure that the function call allocates enough memory for that type.\n\n\n## Example\n\n```cpp\n#define RECORD_SIZE 30 //incorrect or outdated size for record\ntypedef struct {\n\tchar name[30];\n\tint status;\n} Record;\n\nvoid f() {\n\tRecord* p = malloc(RECORD_SIZE); //not of sufficient size to hold a Record\n\t...\n}\n\n```\n\n## References\n* I. Gerg. *An Overview and Example of the Buffer-Overflow Exploit*. IANewsletter vol 7 no 4. 2005.\n* M. Donaldson. *Inside the Buffer Overflow Attack: Mechanism, Method & Prevention*. SANS Institute InfoSec Reading Room. 2002.\n* Common Weakness Enumeration: [CWE-131](https://cwe.mitre.org/data/definitions/131.html).\n* Common Weakness Enumeration: [CWE-122](https://cwe.mitre.org/data/definitions/122.html).\n", + "markdown" : "# Not enough memory allocated for pointer type\nWhen you allocate an array from memory using `malloc`, `calloc` or `realloc`, you should ensure that you allocate enough memory to contain an instance of the required pointer type. Calls that are assigned to a non-void pointer variable, but do not allocate enough memory will cause a buffer overflow when a field accessed on the pointer points to memory that is beyond the allocated array. Buffer overflows can lead to anything from a segmentation fault to a security vulnerability.\n\n\n## Recommendation\nThe highlighted call allocates memory that is too small to contain an instance of the type of the pointer, which can cause a memory overrun. Use the `sizeof` operator to ensure that the function call allocates enough memory for that type.\n\n\n## Example\n\n```cpp\n#define RECORD_SIZE 30 //incorrect or outdated size for record\ntypedef struct {\n\tchar name[30];\n\tint status;\n} Record;\n\nvoid f() {\n\tRecord* p = malloc(RECORD_SIZE); //not of sufficient size to hold a Record\n\t...\n}\n\n```\n\n## References\n* I. Gerg. *An Overview and Example of the Buffer-Overflow Exploit*. IANewsletter vol 7 no 4. 2005.\n* M. Donaldson. *Inside the Buffer Overflow Attack: Mechanism, Method & Prevention*. SANS Institute InfoSec Reading Room. 2002.\n* Common Weakness Enumeration: [CWE-131](https://cwe.mitre.org/data/definitions/131.html).\n* Common Weakness Enumeration: [CWE-122](https://cwe.mitre.org/data/definitions/122.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "security", "external/cwe/cwe-131", "external/cwe/cwe-122" ], + "description" : "Calling 'malloc', 'calloc' or 'realloc' without allocating enough memory to contain\n an instance of the type of the pointer may result in a buffer overflow", + "id" : "cpp/allocation-too-small", + "kind" : "problem", + "name" : "Not enough memory allocated for pointer type", + "precision" : "medium", + "problem.severity" : "warning", + "security-severity" : "8.1" + } + }, { + "id" : "cpp/suspicious-allocation-size", + "name" : "cpp/suspicious-allocation-size", + "shortDescription" : { + "text" : "Not enough memory allocated for array of pointer type" + }, + "fullDescription" : { + "text" : "Calling 'malloc', 'calloc' or 'realloc' without allocating enough memory to contain multiple instances of the type of the pointer may result in a buffer overflow" + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "help" : { + "text" : "# Not enough memory allocated for array of pointer type\nWhen you allocate an array from memory using `malloc`, `calloc` or `realloc`, you should ensure that you allocate enough memory to contain a multiple of the size of the required pointer type. Calls that are assigned to a non-void pointer variable, but do not allocate enough memory will cause a buffer overflow when a field accessed on the pointer points to memory that is beyond the allocated array. Buffer overflows can lead to anything from a segmentation fault to a security vulnerability.\n\n\n## Recommendation\nThe highlighted call allocates memory that is not a multiple of the size of the pointer type, which can cause a memory overrun. Use the `sizeof` operator to ensure that the function call allocates enough memory for that type.\n\n\n## Example\n\n```cpp\n#define RECORD_SIZE 30 //incorrect or outdated size for record\ntypedef struct {\n\tchar name[30];\n\tint status;\n} Record;\n\nvoid f() {\n\tRecord* p = malloc(RECORD_SIZE * 4); //wrong: not a multiple of the size of Record\n\tp[3].status = 1; //will most likely segfault\n\t...\n}\n\n```\n\n## References\n* I. Gerg. *An Overview and Example of the Buffer-Overflow Exploit*. IANewsletter vol 7 no 4. 2005.\n* M. Donaldson. *Inside the Buffer Overflow Attack: Mechanism, Method & Prevention*. SANS Institute InfoSec Reading Room. 2002.\n* Common Weakness Enumeration: [CWE-131](https://cwe.mitre.org/data/definitions/131.html).\n* Common Weakness Enumeration: [CWE-122](https://cwe.mitre.org/data/definitions/122.html).\n", + "markdown" : "# Not enough memory allocated for array of pointer type\nWhen you allocate an array from memory using `malloc`, `calloc` or `realloc`, you should ensure that you allocate enough memory to contain a multiple of the size of the required pointer type. Calls that are assigned to a non-void pointer variable, but do not allocate enough memory will cause a buffer overflow when a field accessed on the pointer points to memory that is beyond the allocated array. Buffer overflows can lead to anything from a segmentation fault to a security vulnerability.\n\n\n## Recommendation\nThe highlighted call allocates memory that is not a multiple of the size of the pointer type, which can cause a memory overrun. Use the `sizeof` operator to ensure that the function call allocates enough memory for that type.\n\n\n## Example\n\n```cpp\n#define RECORD_SIZE 30 //incorrect or outdated size for record\ntypedef struct {\n\tchar name[30];\n\tint status;\n} Record;\n\nvoid f() {\n\tRecord* p = malloc(RECORD_SIZE * 4); //wrong: not a multiple of the size of Record\n\tp[3].status = 1; //will most likely segfault\n\t...\n}\n\n```\n\n## References\n* I. Gerg. *An Overview and Example of the Buffer-Overflow Exploit*. IANewsletter vol 7 no 4. 2005.\n* M. Donaldson. *Inside the Buffer Overflow Attack: Mechanism, Method & Prevention*. SANS Institute InfoSec Reading Room. 2002.\n* Common Weakness Enumeration: [CWE-131](https://cwe.mitre.org/data/definitions/131.html).\n* Common Weakness Enumeration: [CWE-122](https://cwe.mitre.org/data/definitions/122.html).\n" + }, + "properties" : { + "tags" : [ "reliability", "security", "external/cwe/cwe-131", "external/cwe/cwe-122" ], + "description" : "Calling 'malloc', 'calloc' or 'realloc' without allocating enough memory to contain\n multiple instances of the type of the pointer may result in a buffer overflow", + "id" : "cpp/suspicious-allocation-size", + "kind" : "problem", + "name" : "Not enough memory allocated for array of pointer type", + "precision" : "medium", + "problem.severity" : "warning", + "security-severity" : "8.1" + } + }, { + "id" : "cpp/summary/lines-of-user-code", + "name" : "cpp/summary/lines-of-user-code", + "shortDescription" : { + "text" : "Total lines of user written C/C++ code in the database" + }, + "fullDescription" : { + "text" : "The total number of lines of C/C++ code from the source code directory, excluding auto-generated files. This query counts the lines of code, excluding whitespace or comments. Note: If external libraries are included in the codebase either in a checked-in virtual environment or as vendored code, that will currently be counted as user written code." + }, + "defaultConfiguration" : { + "enabled" : true + }, + "properties" : { + "tags" : [ "summary", "lines-of-code" ], + "description" : "The total number of lines of C/C++ code from the source code directory, excluding auto-generated files. This query counts the lines of code, excluding whitespace or comments. Note: If external libraries are included in the codebase either in a checked-in virtual environment or as vendored code, that will currently be counted as user written code.", + "id" : "cpp/summary/lines-of-user-code", + "kind" : "metric", + "name" : "Total lines of user written C/C++ code in the database" + } + }, { + "id" : "cpp/summary/lines-of-code", + "name" : "cpp/summary/lines-of-code", + "shortDescription" : { + "text" : "Total lines of C/C++ code in the database" + }, + "fullDescription" : { + "text" : "The total number of lines of C/C++ code across all files, including system headers, libraries, and auto-generated files. This is a useful metric of the size of a database. For all files that were seen during the build, this query counts the lines of code, excluding whitespace or comments." + }, + "defaultConfiguration" : { + "enabled" : true + }, + "properties" : { + "tags" : [ "summary", "telemetry" ], + "description" : "The total number of lines of C/C++ code across all files, including system headers, libraries, and auto-generated files. This is a useful metric of the size of a database. For all files that were seen during the build, this query counts the lines of code, excluding whitespace or comments.", + "id" : "cpp/summary/lines-of-code", + "kind" : "metric", + "name" : "Total lines of C/C++ code in the database" + } + } ], + "locations" : [ { + "uri" : "file:///home/runner/work/_temp/415fce6e-91c6-440b-98c5-dd2b77fa9470/codeql/qlpacks/codeql/cpp-queries/0.9.1/", + "description" : { + "text" : "The QL pack root directory." + } + }, { + "uri" : "file:///home/runner/work/_temp/415fce6e-91c6-440b-98c5-dd2b77fa9470/codeql/qlpacks/codeql/cpp-queries/0.9.1/qlpack.yml", + "description" : { + "text" : "The QL pack definition file." + } + } ] + } ] + }, + "invocations" : [ { + "toolExecutionNotifications" : [ { + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "main.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + } + } + } ], + "message" : { + "text" : "File successfully extracted." + }, + "level" : "none", + "descriptor" : { + "id" : "cpp/diagnostics/successfully-extracted-files", + "index" : 1, + "toolComponent" : { + "index" : 0 + } + }, + "properties" : { + "formattedMessage" : { + "text" : "File successfully extracted." + } + } + }, { + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "main.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + } + } + } ], + "message" : { + "text" : "" + }, + "level" : "none", + "descriptor" : { + "id" : "cli/expected-extracted-files/c", + "index" : 0 + }, + "properties" : { + "formattedMessage" : { + "text" : "" + } + } + }, { + "message" : { + "text" : "" + }, + "level" : "note", + "timeUtc" : "2024-02-22T21:47:53.000+00:00", + "descriptor" : { + "id" : "cpp/autobuilder/deptrace", + "index" : 1 + }, + "properties" : { + "attributes" : { + "failedPackages" : [ ], + "installedPackages" : [ "libc6-dev-amd64-cross", "libgcc-s1-amd64-cross" ] + }, + "visibility" : { + "statusPage" : false, + "telemetry" : true + } + } + } ], + "executionSuccessful" : true + } ], + "artifacts" : [ { + "location" : { + "uri" : "main.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + } + } ], + "results" : [ ], + "columnKind" : "utf16CodeUnits", + "properties" : { + "codeqlConfigSummary" : { + "disableDefaultQueries" : false, + "queries" : [ { + "type" : "builtinSuite", + "uses" : "security-extended" + } ] + }, + "metricResults" : [ { + "rule" : { + "id" : "cpp/summary/lines-of-user-code", + "index" : 80, + "toolComponent" : { + "index" : 0 + } + }, + "ruleId" : "cpp/summary/lines-of-user-code", + "value" : 5 + }, { + "rule" : { + "id" : "cpp/summary/lines-of-code", + "index" : 81, + "toolComponent" : { + "index" : 0 + } + }, + "ruleId" : "cpp/summary/lines-of-code", + "value" : 5 + } ], + "semmle.formatSpecifier" : "sarif-latest" + } + } ] +} \ No newline at end of file From 8d6c207f3aa504956d247ff3aecb2c0e41efcf32 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Fri, 23 Feb 2024 10:22:57 -0500 Subject: [PATCH 40/44] diffing --- ...internal-pr-bundle-integration-test-cpp.yml | 18 ++++++++++++++---- .../cpp/expected.sarif | 0 .../cpp/src/Makefile | 0 .../cpp/src/main.c | 0 4 files changed, 14 insertions(+), 4 deletions(-) rename {integration => integration-tests}/cpp/expected.sarif (100%) rename {integration => integration-tests}/cpp/src/Makefile (100%) rename {integration => integration-tests}/cpp/src/main.c (100%) diff --git a/.github/workflows/internal-pr-bundle-integration-test-cpp.yml b/.github/workflows/internal-pr-bundle-integration-test-cpp.yml index 67de39f..5a2e729 100644 --- a/.github/workflows/internal-pr-bundle-integration-test-cpp.yml +++ b/.github/workflows/internal-pr-bundle-integration-test-cpp.yml @@ -66,13 +66,13 @@ jobs: with: languages: ${{ matrix.language }} queries: security-extended - source-root: integration/cpp/src/ # Path containing the example application + source-root: integration-tests/cpp/src/ # Path containing the example application tools: ${{ env.QLT_CODEQL_HOME }}/../out/codeql-bundle.tar.gz - name: Autobuild uses: github/codeql-action/autobuild@v2 with: - working-directory: integration/cpp/src/ # Path containing the example application + working-directory: integration-tests/cpp/src/ # Path containing the example application - name: Perform CodeQL Analysis id: analysis @@ -93,13 +93,23 @@ jobs: ${{ steps.analysis.outputs.sarif-output }}/*.sarif if-no-files-found: error + - name: Upload Bundle Used + uses: actions/upload-artifact@v2 + with: + name: codeql-bundle.tar.gz + path: | + ${{ env.QLT_CODEQL_HOME }}/../out/codeql-bundle.tar.gz + if-no-files-found: error + - name: Validate SARIF Results shell: bash run: | # Compare the expected vs the actual - - if ! diff integration/cpp/expected.sarif ${{ steps.analysis.outputs.sarif-output }}/cpp.sarif ; then + cat integration-tests/cpp/expected.sarif | jq '.runs' > integration-tests/cpp/expected + cat ${{ steps.analysis.outputs.sarif-output }}/cpp.sarif | jq '.runs' > integration-tests/cpp/actual + + if ! diff integration-tests/cpp/expected integration-tests/cpp/actual ; then echo "Expected file does not match actual. Please check the SARIF file for differences." exit 1 fi diff --git a/integration/cpp/expected.sarif b/integration-tests/cpp/expected.sarif similarity index 100% rename from integration/cpp/expected.sarif rename to integration-tests/cpp/expected.sarif diff --git a/integration/cpp/src/Makefile b/integration-tests/cpp/src/Makefile similarity index 100% rename from integration/cpp/src/Makefile rename to integration-tests/cpp/src/Makefile diff --git a/integration/cpp/src/main.c b/integration-tests/cpp/src/main.c similarity index 100% rename from integration/cpp/src/main.c rename to integration-tests/cpp/src/main.c From 4cc2367784c18156c101497aece86ea56342fd16 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Fri, 23 Feb 2024 10:32:21 -0500 Subject: [PATCH 41/44] work --- .../internal-pr-bundle-integration-test-cpp.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/internal-pr-bundle-integration-test-cpp.yml b/.github/workflows/internal-pr-bundle-integration-test-cpp.yml index 5a2e729..e7f2d35 100644 --- a/.github/workflows/internal-pr-bundle-integration-test-cpp.yml +++ b/.github/workflows/internal-pr-bundle-integration-test-cpp.yml @@ -93,13 +93,13 @@ jobs: ${{ steps.analysis.outputs.sarif-output }}/*.sarif if-no-files-found: error - - name: Upload Bundle Used - uses: actions/upload-artifact@v2 - with: - name: codeql-bundle.tar.gz - path: | - ${{ env.QLT_CODEQL_HOME }}/../out/codeql-bundle.tar.gz - if-no-files-found: error + # - name: Upload Bundle Used + # uses: actions/upload-artifact@v2 + # with: + # name: codeql-bundle.tar.gz + # path: | + # ${{ env.QLT_CODEQL_HOME }}/../out/codeql-bundle.tar.gz + # if-no-files-found: error - name: Validate SARIF Results From 5381b835e81e0314a700c70ae37519ff376dbb79 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Thu, 29 Feb 2024 17:26:00 -0500 Subject: [PATCH 42/44] adding SARIF compare tool --- .../Bundle/Commands/BundleCommandFeature.cs | 37 ++-- .../CodeQLToolkit.Features.csproj | 1 - src/CodeQLToolkit.Shared/Types/SARIFResult.cs | 209 ++++++++++++++++++ 3 files changed, 230 insertions(+), 17 deletions(-) create mode 100644 src/CodeQLToolkit.Shared/Types/SARIFResult.cs diff --git a/src/CodeQLToolkit.Features/Bundle/Commands/BundleCommandFeature.cs b/src/CodeQLToolkit.Features/Bundle/Commands/BundleCommandFeature.cs index e8b12ea..f6fb7fc 100644 --- a/src/CodeQLToolkit.Features/Bundle/Commands/BundleCommandFeature.cs +++ b/src/CodeQLToolkit.Features/Bundle/Commands/BundleCommandFeature.cs @@ -1,4 +1,5 @@ -using CodeQLToolkit.Shared.Utils; +using CodeQLToolkit.Features.Bundle.Commands.Targets; +using CodeQLToolkit.Shared.Utils; using System; using System.Collections.Generic; using System.CommandLine; @@ -35,29 +36,33 @@ public void Register(Command parentCommand) var runCommand = new Command("run", "Functions pertaining running bundle commands."); - parentCommand.Add(runCommand); - //var checkQueryQueriesCommand = new Command("check-queries", "Checks the query metadata for the specified language."); - //var languageOption = new Option("--language", $"The language to run tests for.") { IsRequired = true }.FromAmong(SupportedLangauges.Select(x => x.ToOptionString()).ToArray()); + var validateIntegrationTestsCommand = new Command("validate-integration-tests", "Validates the results of an integration test using a semantic diff."); + var expectedOption = new Option("--expected", "The SARIF file containing the expected results.") { IsRequired = true }; + var actualOption = new Option("--actual", "The SARIF file containing the actual results.") { IsRequired = true }; + + validateIntegrationTestsCommand.Add(expectedOption); + validateIntegrationTestsCommand.Add(actualOption); - //checkQueryQueriesCommand.Add(languageOption); + runCommand.Add(validateIntegrationTestsCommand); + + parentCommand.Add(runCommand); - //runCommand.Add(checkQueryQueriesCommand); + validateIntegrationTestsCommand.SetHandler((basePath, expected, actual) => + { + Log.G().LogInformation("Executing validate-integration-tests command..."); - //checkQueryQueriesCommand.SetHandler((language, basePath, prettyPrint) => - //{ - // Log.G().LogInformation("Executing check-query-metadata command..."); + new ValidateIntegrationTestResults() + { + Base = basePath, + Expected = expected, + Actual = actual - // new CheckQueriesCommandTarget() - // { - // Base = basePath, - // Language = language, - // PrettyPrint = prettyPrint, - // }.Run(); + }.Run(); - //}, languageOption, Globals.BasePathOption, prettyPrintOption); + },Globals.BasePathOption, expectedOption, actualOption); } public int Run() diff --git a/src/CodeQLToolkit.Features/CodeQLToolkit.Features.csproj b/src/CodeQLToolkit.Features/CodeQLToolkit.Features.csproj index efc6fed..df18b6d 100644 --- a/src/CodeQLToolkit.Features/CodeQLToolkit.Features.csproj +++ b/src/CodeQLToolkit.Features/CodeQLToolkit.Features.csproj @@ -11,7 +11,6 @@ - diff --git a/src/CodeQLToolkit.Shared/Types/SARIFResult.cs b/src/CodeQLToolkit.Shared/Types/SARIFResult.cs new file mode 100644 index 0000000..fbf3cc0 --- /dev/null +++ b/src/CodeQLToolkit.Shared/Types/SARIFResult.cs @@ -0,0 +1,209 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CodeQLToolkit.Shared.Types +{ + public class SARIFResult + { + public Run[] runs { get; set; } + public string schema { get; set; } + public string version { get; set; } + + public string? RawSARIF { get; set; } + } + + public class Run + { + public Artifact[] artifacts { get; set; } + public Automationdetails automationDetails { get; set; } + public Conversion conversion { get; set; } + public Result[] results { get; set; } + public Tool1 tool { get; set; } + public Versioncontrolprovenance[] versionControlProvenance { get; set; } + } + + public class Automationdetails + { + public string id { get; set; } + } + + public class Conversion + { + public Tool tool { get; set; } + } + + public class Tool + { + public Driver driver { get; set; } + } + + public class Driver + { + public string name { get; set; } + } + + public class Tool1 + { + public Driver1 driver { get; set; } + public Extension[] extensions { get; set; } + } + + public class Driver1 + { + public string name { get; set; } + public string semanticVersion { get; set; } + } + + public class Extension + { + public string name { get; set; } + public string semanticVersion { get; set; } + public Rule[] rules { get; set; } + } + + public class Rule + { + public Defaultconfiguration defaultConfiguration { get; set; } + public Fulldescription fullDescription { get; set; } + public Help help { get; set; } + public string id { get; set; } + public string name { get; set; } + public Properties properties { get; set; } + public Shortdescription shortDescription { get; set; } + } + + public class Defaultconfiguration + { + public string level { get; set; } + } + + public class Fulldescription + { + public string text { get; set; } + } + + public class Help + { + public string markdown { get; set; } + public string text { get; set; } + } + + public class Properties + { + public string precision { get; set; } + public string queryURI { get; set; } + public string securityseverity { get; set; } + public string[] tags { get; set; } + } + + public class Shortdescription + { + public string text { get; set; } + } + + public class Artifact + { + public Location location { get; set; } + } + + public class Location + { + public int index { get; set; } + public string uri { get; set; } + } + + public class Result + { + public string correlationGuid { get; set; } + public string level { get; set; } + + public Location1[] locations { get; set; } + + public string LocationsString() + { + + List _locations = new List(); + + foreach (var location in locations) + { + _locations.Add(location.ToString()); + } + + return string.Join(", ", _locations); + + } + public Message message { get; set; } + public Partialfingerprints partialFingerprints { get; set; } + public Properties1 properties { get; set; } + public Rule1 rule { get; set; } + public string ruleId { get; set; } + } + + public class Message + { + public string text { get; set; } + } + + public class Partialfingerprints + { + public string primaryLocationLineHash { get; set; } + } + + public class Properties1 + { + public int githubalertNumber { get; set; } + public string githubalertUrl { get; set; } + } + + public class Rule1 + { + public string id { get; set; } + public Toolcomponent toolComponent { get; set; } + public int index { get; set; } + } + + public class Toolcomponent + { + public int index { get; set; } + } + + public class Location1 + { + public Physicallocation physicalLocation { get; set; } + + public override string ToString() + { + return $"{physicalLocation.artifactLocation.uri}:{physicalLocation.region.startLine}:{physicalLocation.region.startColumn}-{physicalLocation.region.endLine}:{physicalLocation.region.endColumn}"; + } + } + + public class Physicallocation + { + public Artifactlocation artifactLocation { get; set; } + public Region region { get; set; } + } + + public class Artifactlocation + { + public int index { get; set; } + public string uri { get; set; } + } + + public class Region + { + public int endColumn { get; set; } + public int endLine { get; set; } + public int startColumn { get; set; } + public int startLine { get; set; } + } + + public class Versioncontrolprovenance + { + public string branch { get; set; } + public string repositoryUri { get; set; } + public string revisionId { get; set; } + } +} From 1d626b87216be98e90bebe50153691c0a7ecbade Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Thu, 29 Feb 2024 17:27:48 -0500 Subject: [PATCH 43/44] update workflow --- ...nternal-pr-bundle-integration-test-cpp.yml | 8 +- .../Targets/ValidateIntegrationTestResults.cs | 179 ++++++++++++++++++ 2 files changed, 180 insertions(+), 7 deletions(-) create mode 100644 src/CodeQLToolkit.Features/Bundle/Commands/Targets/ValidateIntegrationTestResults.cs diff --git a/.github/workflows/internal-pr-bundle-integration-test-cpp.yml b/.github/workflows/internal-pr-bundle-integration-test-cpp.yml index e7f2d35..48adc3e 100644 --- a/.github/workflows/internal-pr-bundle-integration-test-cpp.yml +++ b/.github/workflows/internal-pr-bundle-integration-test-cpp.yml @@ -106,10 +106,4 @@ jobs: shell: bash run: | # Compare the expected vs the actual - cat integration-tests/cpp/expected.sarif | jq '.runs' > integration-tests/cpp/expected - cat ${{ steps.analysis.outputs.sarif-output }}/cpp.sarif | jq '.runs' > integration-tests/cpp/actual - - if ! diff integration-tests/cpp/expected integration-tests/cpp/actual ; then - echo "Expected file does not match actual. Please check the SARIF file for differences." - exit 1 - fi + qlt bundle run validate-integration-tests --expected integration-tests/cpp/expected.sarif --actual ${{ steps.analysis.outputs.sarif-output }}/cpp.sarif \ No newline at end of file diff --git a/src/CodeQLToolkit.Features/Bundle/Commands/Targets/ValidateIntegrationTestResults.cs b/src/CodeQLToolkit.Features/Bundle/Commands/Targets/ValidateIntegrationTestResults.cs new file mode 100644 index 0000000..850d699 --- /dev/null +++ b/src/CodeQLToolkit.Features/Bundle/Commands/Targets/ValidateIntegrationTestResults.cs @@ -0,0 +1,179 @@ +using CodeQLToolkit.Features.CodeQL.Commands.Targets; +using CodeQLToolkit.Features.Validation; +using Newtonsoft.Json; +using NLog; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CodeQLToolkit.Features.Bundle.Commands.Targets +{ + public class ValidateIntegrationTestResults : CommandTarget + { + public string Expected { get; set; } + public string Actual { get; set; } + + public override void Run() + { + Log.G().LogInformation($"Running validate-integration-tests command"); + + // First load up the expected and the actuals. + if (!File.Exists(Expected)) + { + DieWithError($"Expected file {Expected} does not exist."); + } + + if(!File.Exists(Actual)) + { + DieWithError($"Actual file {Actual} does not exist."); + } + + try + { + var expectedSARIF = JsonConvert.DeserializeObject(File.ReadAllText(Expected)); + var actualSARIF = JsonConvert.DeserializeObject(File.ReadAllText(Actual)); + + + // Check 1: Ensure they contain the same number of results. This is a pretty signifigant error so we don't report + // past this point. + if (expectedSARIF.runs.Length != actualSARIF.runs.Length) + { + DieWithError($"Number of runs does not match. Expected: {expectedSARIF.runs.Length}, Actual: {actualSARIF.runs.Length}"); + } + + // Check 2: For each run, check if the number of results match. + for (var i = 0; i < expectedSARIF.runs.Length; i++) + { + if (expectedSARIF.runs[i].results.Length != actualSARIF.runs[i].results.Length) + { + Log.G().LogWarning($"Number of results does not match. Expected: {expectedSARIF.runs[i].results.Length}, Actual: {actualSARIF.runs[i].results.Length}"); + } + } + + // Check 3: Build up some mappings to compare exactly what is missing. + var actualDict = ExtractResultMap(actualSARIF); + var expectedDict = ExtractResultMap(expectedSARIF); + + + // Populate the differences. + var resultsInExpectedNotInActual = FindMissingResults(expectedSARIF, actualDict); + var resultsInActualNotInExpected = FindMissingResults(actualSARIF, expectedDict); + + // Report results. + if(resultsInExpectedNotInActual.Count == 0 && resultsInActualNotInExpected.Count == 0) + { + Log.G().LogInformation($"SARIF results identical."); + } + else + { + Log.G().LogInformation($"SARIF results not identical. Please see below for full report."); + + Log.G().LogInformation($"SARIF Results in EXPECTED not contained in ACTUAL"); + Log.G().LogInformation($"------------------------------------------------------"); + + foreach (var r in resultsInExpectedNotInActual) + { + + Log.G().LogInformation($"Rule: {r.ruleId} @ Location {r.LocationsString()} with Message \"{r.message.text}\" exists in EXPECTED but is missing from ACTUAL."); + + } + + Log.G().LogInformation($"SARIF Results in ACTUAL not contained in EXPECTED"); + Log.G().LogInformation($"------------------------------------------------------"); + + foreach (var r in resultsInActualNotInExpected) + { + Log.G().LogInformation($"Rule: {r.ruleId} @ Location {r.LocationsString()} with Message \"{r.message.text}\" exists in ACTUAL but is missing from EXPECTED."); + } + + DieWithError("SARIF results NOT identical. Results reported, above."); + } + + } + catch (Exception e) + { + Log.G().LogError(e.Message); + DieWithError("Error decoding SARIF files. Please check the format and try again."); + } + + List FindMissingResults(SARIFResult? expectedSARIF, Dictionary> actualDict) + { + var resultsNotFound = new List(); + + for (var i = 0; i < expectedSARIF.runs.Length; i++) + { + for (var j = 0; j < expectedSARIF.runs[i].results.Length; j++) + { + var result = expectedSARIF.runs[i].results[j]; + + if (!actualDict.ContainsKey(result.ruleId)) + { + resultsNotFound.Add(result); + } + else + { + if (!ContainsResult(result, actualDict[result.ruleId])) + { + resultsNotFound.Add(result); + } + } + } + + } + + return resultsNotFound; + } + } + + private bool ContainsResult(Result searchFor, List inResults) + { + foreach(var result in inResults) + { + if(ResultMatches(result, searchFor)) + { + return true; + } + } + + return false; + } + + private bool ResultMatches(Result a, Result b) + { + if(a.ruleId == b.ruleId && a.message.text == b.message.text && a.LocationsString() == b.LocationsString()) + { + return true; + } + + return false; + } + + private Dictionary> ExtractResultMap(SARIFResult? sarif) + { + Dictionary> dict = new Dictionary>(); + + for (var i = 0; i < sarif.runs.Length; i++) + { + for (var j = 0; j < sarif.runs[i].results.Length; j++) + { + var result = sarif.runs[i].results[j]; + + if (dict.ContainsKey(result.ruleId)) + { + dict[result.ruleId].Add(result); + } + else + { + dict[result.ruleId] = new List() + { + result + }; + } + } + } + return dict; + } + } +} From 48cfab61a7b7085f0a4a2b38b2c5607be46f11f1 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Fri, 1 Mar 2024 09:47:32 -0500 Subject: [PATCH 44/44] add bundle to flow --- .../internal-pr-bundle-integration-test-cpp.yml | 17 ++++++++--------- .../CodeQL/Commands/Targets/InstallCommand.cs | 9 +++++++++ src/CodeQLToolkit.Shared/CodeQL/RESOLUTION.md | 3 ++- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/.github/workflows/internal-pr-bundle-integration-test-cpp.yml b/.github/workflows/internal-pr-bundle-integration-test-cpp.yml index 48adc3e..e23c736 100644 --- a/.github/workflows/internal-pr-bundle-integration-test-cpp.yml +++ b/.github/workflows/internal-pr-bundle-integration-test-cpp.yml @@ -67,7 +67,7 @@ jobs: languages: ${{ matrix.language }} queries: security-extended source-root: integration-tests/cpp/src/ # Path containing the example application - tools: ${{ env.QLT_CODEQL_HOME }}/../out/codeql-bundle.tar.gz + tools: ${{ env.QLT_CODEQL_BUNDLE_PATH }} - name: Autobuild uses: github/codeql-action/autobuild@v2 @@ -93,14 +93,13 @@ jobs: ${{ steps.analysis.outputs.sarif-output }}/*.sarif if-no-files-found: error - # - name: Upload Bundle Used - # uses: actions/upload-artifact@v2 - # with: - # name: codeql-bundle.tar.gz - # path: | - # ${{ env.QLT_CODEQL_HOME }}/../out/codeql-bundle.tar.gz - # if-no-files-found: error - + - name: Upload Bundle Used + uses: actions/upload-artifact@v2 + with: + name: codeql-bundle.tar.gz + path: | + ${{ env.QLT_CODEQL_BUNDLE_PATH }} + if-no-files-found: error - name: Validate SARIF Results shell: bash diff --git a/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs b/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs index 1c1abb0..e649b41 100644 --- a/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs +++ b/src/CodeQLToolkit.Features/CodeQL/Commands/Targets/InstallCommand.cs @@ -58,13 +58,22 @@ public override void Run() Environment.SetEnvironmentVariable("QLT_CODEQL_HOME", installation.CodeQLHome); Environment.SetEnvironmentVariable("QLT_CODEQL_PATH", installation.CodeQLToolBinary); + if (CustomBundles || QuickBundles) + { + Environment.SetEnvironmentVariable("QLT_CODEQL_BUNDLE_PATH", installation.CustomBundleOutputBundle); + } if (AutomationTypeHelper.AutomationTypeFromString(AutomationTarget) == AutomationType.ACTIONS) { if (Environment.GetEnvironmentVariable("GITHUB_ENV") != null && File.Exists(Environment.GetEnvironmentVariable("GITHUB_ENV"))) { + File.AppendAllText(Environment.GetEnvironmentVariable("GITHUB_ENV"), $"QLT_CODEQL_HOME={installation.CodeQLHome}" + "\n"); File.AppendAllText(Environment.GetEnvironmentVariable("GITHUB_ENV"), $"QLT_CODEQL_PATH={installation.CodeQLToolBinary}" + "\n"); + if (CustomBundles || QuickBundles) + { + File.AppendAllText(Environment.GetEnvironmentVariable("GITHUB_ENV"), $"QLT_CODEQL_BUNDLE_PATH={installation.CustomBundleOutputBundle}" + "\n"); + } } } diff --git a/src/CodeQLToolkit.Shared/CodeQL/RESOLUTION.md b/src/CodeQLToolkit.Shared/CodeQL/RESOLUTION.md index 36bd990..b8fef98 100644 --- a/src/CodeQLToolkit.Shared/CodeQL/RESOLUTION.md +++ b/src/CodeQLToolkit.Shared/CodeQL/RESOLUTION.md @@ -47,9 +47,10 @@ For a bundle installation the mapping is as follows: - `CodeQLCLIBundle` - The bundle downloaded from `github/codeql-action/releases` to base the bundle on. -In all cases, at the end of the execution two environment variables are set: +In all cases, at the end of the execution two to three environment variables are set: - `QLT_CODEQL_PATH` - The path to the CodeQL binary. - `QLT_CODEQL_HOME` - The root installation of CodeQL +- `QLT_CODEQL_BUNDLE_PATH` - The path to the bundle created by QLT. ## Idents within the Installation Directory