mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-05-01 13:13:53 +00:00
fix: issue found by fuzzing (#846)
* Add the beginnings of a fuzzing system for CLI11. This commit adds the fuzzing code, a simple test, and two fixes to issues(seg faults) found by the initial round of fuzzing. It also adds a few tests and coverage issues uncovered in the process of developing the fuzz tests. As a side effect adjusts some of the azure tests to specify the vmImage which was being changed on azure. * update license to match rest of code base --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
fc9cea6d76
commit
0a615f854d
@ -4,3 +4,4 @@ ignore:
|
|||||||
- "book"
|
- "book"
|
||||||
- "docs"
|
- "docs"
|
||||||
- "test_package"
|
- "test_package"
|
||||||
|
- "fuzz"
|
||||||
|
2
.github/codecov.yml
vendored
2
.github/codecov.yml
vendored
@ -1,6 +1,6 @@
|
|||||||
codecov:
|
codecov:
|
||||||
notify:
|
notify:
|
||||||
after_n_builds: 4
|
after_n_builds: 8
|
||||||
coverage:
|
coverage:
|
||||||
status:
|
status:
|
||||||
project:
|
project:
|
||||||
|
54
.github/workflows/fuzz.yml
vendored
Normal file
54
.github/workflows/fuzz.yml
vendored
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
name: Fuzz
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- v*
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
quick_fuzz1:
|
||||||
|
name: quickfuzz1
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Configure
|
||||||
|
run: |
|
||||||
|
cmake -S . -B build \
|
||||||
|
-DCMAKE_CXX_STANDARD=17 \
|
||||||
|
-DCLI11_SINGLE_FILE_TESTS=OFF \
|
||||||
|
-DCLI11_BUILD_EXAMPLES=OFF \
|
||||||
|
-DCLI11_FUZZ_TARGET=ON \
|
||||||
|
-DCLI11_BUILD_TESTS=OFF \
|
||||||
|
-DCLI11_BUILD_DOCS=OFF \
|
||||||
|
-DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_COMPILER_FORCED=ON \
|
||||||
|
-DCMAKE_CXX_FLAGS="-g -O1 -fsanitize=fuzzer,undefined,address"
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: cmake --build build -j4
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
|
make QUICK_CLI11_APP_FUZZ
|
||||||
|
|
||||||
|
- name: Test2
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
|
make QUICK_CLI11_FILE_FUZZ
|
||||||
|
|
||||||
|
|
||||||
|
- name: artifacts
|
||||||
|
if: failure()
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: file_failure
|
||||||
|
path: ./build/fuzz/cli11_*_fail_artifact.txt
|
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@ -48,7 +48,7 @@ jobs:
|
|||||||
- name: Prepare coverage
|
- name: Prepare coverage
|
||||||
run: |
|
run: |
|
||||||
lcov --directory . --capture --output-file coverage.info
|
lcov --directory . --capture --output-file coverage.info
|
||||||
lcov --remove coverage.info '*/tests/*' '*/examples/*' '/usr/*' '*/book/*' --output-file coverage.info
|
lcov --remove coverage.info '*/tests/*' '*/examples/*' '/usr/*' '*/book/*' '*/fuzz/*' --output-file coverage.info
|
||||||
lcov --list coverage.info
|
lcov --list coverage.info
|
||||||
working-directory: build
|
working-directory: build
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
exclude: ^(.github/workflows/|docs/img/)
|
||||||
ci:
|
ci:
|
||||||
autoupdate_commit_msg: "chore(deps): pre-commit.ci autoupdate"
|
autoupdate_commit_msg: "chore(deps): pre-commit.ci autoupdate"
|
||||||
autofix_commit_msg: "style: pre-commit.ci fixes"
|
autofix_commit_msg: "style: pre-commit.ci fixes"
|
||||||
|
@ -132,6 +132,9 @@ endif()
|
|||||||
|
|
||||||
include(CLI11Warnings)
|
include(CLI11Warnings)
|
||||||
|
|
||||||
|
# build the fuzzing example or fuzz entry point
|
||||||
|
add_subdirectory(fuzz)
|
||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
|
||||||
# Allow tests to be run on CUDA
|
# Allow tests to be run on CUDA
|
||||||
|
@ -28,7 +28,6 @@ jobs:
|
|||||||
- bash: cpplint --counting=detailed --recursive examples include/CLI tests
|
- bash: cpplint --counting=detailed --recursive examples include/CLI tests
|
||||||
displayName: Checking against google style guide
|
displayName: Checking against google style guide
|
||||||
|
|
||||||
# TODO: Fix macOS error and windows warning in c++17 mode
|
|
||||||
- job: Native
|
- job: Native
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
@ -38,13 +37,13 @@ jobs:
|
|||||||
vmImage: "ubuntu-latest"
|
vmImage: "ubuntu-latest"
|
||||||
cli11.precompile: ON
|
cli11.precompile: ON
|
||||||
macOS17:
|
macOS17:
|
||||||
vmImage: "macOS-latest"
|
vmImage: "macOS-12"
|
||||||
cli11.std: 17
|
cli11.std: 17
|
||||||
macOS11:
|
macOS11:
|
||||||
vmImage: "macOS-latest"
|
vmImage: "macOS-11"
|
||||||
cli11.std: 11
|
cli11.std: 11
|
||||||
macOS11PC:
|
macOS11PC:
|
||||||
vmImage: "macOS-latest"
|
vmImage: "macOS-11"
|
||||||
cli11.std: 11
|
cli11.std: 11
|
||||||
cli11.precompile: ON
|
cli11.precompile: ON
|
||||||
Windows17:
|
Windows17:
|
||||||
|
48
fuzz/CMakeLists.txt
Normal file
48
fuzz/CMakeLists.txt
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# Copyright (c) 2017-2023, University of Cincinnati, developed by Henry Schreiner
|
||||||
|
# under NSF AWARD 1414736 and by the respective contributors.
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
if(CMAKE_CXX_STANDARD GREATER 16)
|
||||||
|
if(CLI11_FUZZ_TARGET)
|
||||||
|
|
||||||
|
add_executable(cli11_app_fuzzer cli11_app_fuzz.cpp fuzzApp.cpp fuzzApp.hpp)
|
||||||
|
target_link_libraries(cli11_app_fuzzer PUBLIC CLI11)
|
||||||
|
set_property(TARGET cli11_app_fuzzer PROPERTY FOLDER "Tests")
|
||||||
|
|
||||||
|
add_executable(cli11_file_fuzzer cli11_file_fuzz.cpp fuzzApp.cpp fuzzApp.hpp)
|
||||||
|
target_link_libraries(cli11_file_fuzzer PUBLIC CLI11)
|
||||||
|
set_property(TARGET cli11_file_fuzzer PROPERTY FOLDER "Tests")
|
||||||
|
|
||||||
|
if(NOT CLI11_FUZZ_ARTIFACT_PATH)
|
||||||
|
set(CLI11_FUZZ_ARTIFACT_PATH ${PROJECT_BINARY_DIR}/fuzz)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT CLI11_FUZZ_TIME)
|
||||||
|
set(CLI11_FUZZ_TIME 360)
|
||||||
|
endif()
|
||||||
|
add_custom_target(
|
||||||
|
QUICK_CLI11_APP_FUZZ
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E make_directory corp
|
||||||
|
COMMAND
|
||||||
|
cli11_app_fuzzer corp -max_total_time=${CLI11_FUZZ_TIME} -max_len=2048
|
||||||
|
-dict=${CMAKE_CURRENT_SOURCE_DIR}/fuzz_dictionary1.txt
|
||||||
|
-exact_artifact_path=${CLI11_FUZZ_ARTIFACT_PATH}/cli11_app_fail_artifact.txt)
|
||||||
|
|
||||||
|
add_custom_target(
|
||||||
|
QUICK_CLI11_FILE_FUZZ
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E make_directory corp
|
||||||
|
COMMAND
|
||||||
|
cli11_file_fuzzer corp -max_total_time=${CLI11_FUZZ_TIME} -max_len=2048
|
||||||
|
-dict=${CMAKE_CURRENT_SOURCE_DIR}/fuzz_dictionary2.txt
|
||||||
|
-exact_artifact_path=${CLI11_FUZZ_ARTIFACT_PATH}/cli11_file_fail_artifact.txt)
|
||||||
|
|
||||||
|
else()
|
||||||
|
if(CLI11_BUILD_EXAMPLES)
|
||||||
|
add_executable(cli11Fuzz fuzzCommand.cpp fuzzApp.cpp fuzzApp.hpp)
|
||||||
|
target_link_libraries(cli11Fuzz PUBLIC CLI11)
|
||||||
|
set_property(TARGET cli11Fuzz PROPERTY FOLDER "Examples")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
30
fuzz/cli11_app_fuzz.cpp
Normal file
30
fuzz/cli11_app_fuzz.cpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) 2017-2023, University of Cincinnati, developed by Henry Schreiner
|
||||||
|
// under NSF AWARD 1414736 and by the respective contributors.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
#include "fuzzApp.hpp"
|
||||||
|
#include <CLI/CLI.hpp>
|
||||||
|
#include <cstring>
|
||||||
|
#include <exception>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||||
|
if(Size == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
std::string parseString(reinterpret_cast<const char *>(Data), Size);
|
||||||
|
|
||||||
|
CLI::FuzzApp fuzzdata;
|
||||||
|
|
||||||
|
auto app = fuzzdata.generateApp();
|
||||||
|
try {
|
||||||
|
app->parse(parseString);
|
||||||
|
} catch(const CLI::ParseError &e) {
|
||||||
|
//(app)->exit(e);
|
||||||
|
// this just indicates we caught an error known by CLI
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0; // Non-zero return values are reserved for future use.
|
||||||
|
}
|
31
fuzz/cli11_file_fuzz.cpp
Normal file
31
fuzz/cli11_file_fuzz.cpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Copyright (c) 2017-2023, University of Cincinnati, developed by Henry Schreiner
|
||||||
|
// under NSF AWARD 1414736 and by the respective contributors.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
#include "fuzzApp.hpp"
|
||||||
|
#include <CLI/CLI.hpp>
|
||||||
|
#include <cstring>
|
||||||
|
#include <exception>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||||
|
if(Size == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
std::string parseString(reinterpret_cast<const char *>(Data), Size);
|
||||||
|
std::stringstream out(parseString);
|
||||||
|
CLI::FuzzApp fuzzdata;
|
||||||
|
|
||||||
|
auto app = fuzzdata.generateApp();
|
||||||
|
try {
|
||||||
|
app->parse_from_stream(out);
|
||||||
|
} catch(const CLI::ParseError &e) {
|
||||||
|
(app)->exit(e);
|
||||||
|
// this just indicates we caught an error known by CLI
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0; // Non-zero return values are reserved for future use.
|
||||||
|
}
|
85
fuzz/fuzzApp.cpp
Normal file
85
fuzz/fuzzApp.cpp
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
// Copyright (c) 2017-2023, University of Cincinnati, developed by Henry Schreiner
|
||||||
|
// under NSF AWARD 1414736 and by the respective contributors.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
#include "fuzzApp.hpp"
|
||||||
|
|
||||||
|
namespace CLI {
|
||||||
|
/*
|
||||||
|
int32_t val32{0};
|
||||||
|
int16_t val16{0};
|
||||||
|
int8_t val8{0};
|
||||||
|
int64_t val64{0};
|
||||||
|
|
||||||
|
uint32_t uval32{0};
|
||||||
|
uint16_t uval16{0};
|
||||||
|
uint8_t uval8{0};
|
||||||
|
uint64_t uval64{0};
|
||||||
|
|
||||||
|
std::atomic<int64_t> atomicval64{0};
|
||||||
|
std::atomic<uint64_t> atomicuval64{0};
|
||||||
|
|
||||||
|
double v1{0};
|
||||||
|
float v2{0};
|
||||||
|
|
||||||
|
std::vector<double> vv1;
|
||||||
|
std::vector<std::string> vstr;
|
||||||
|
std::vector<std::vector<double>> vecvecd;
|
||||||
|
std::vector<std::vector<std::string>> vvs;
|
||||||
|
std::optional<double> od1;
|
||||||
|
std::optional<std::string> ods;
|
||||||
|
std::pair<double, std::string> p1;
|
||||||
|
std::pair<std::vector<double>, std::string> p2;
|
||||||
|
std::tuple<int64_t, uint16_t, std::optional<double>> t1;
|
||||||
|
std::tuple<std::tuple<std::tuple<std::string, double, std::vector<int>>,std::string, double>,std::vector<int>,
|
||||||
|
std::optional<std::string>> tcomplex; std::string_view vstrv;
|
||||||
|
|
||||||
|
bool flag1{false};
|
||||||
|
int flagCnt{0};
|
||||||
|
std::atomic<bool> flagAtomic{false};
|
||||||
|
*/
|
||||||
|
std::shared_ptr<CLI::App> FuzzApp::generateApp() {
|
||||||
|
auto fApp = std::make_shared<CLI::App>("fuzzing App", "fuzzer");
|
||||||
|
fApp->set_config("--config");
|
||||||
|
fApp->add_flag("-a,--flag");
|
||||||
|
fApp->add_flag("-b,--flag2", flag1);
|
||||||
|
fApp->add_flag("-c{34},--flag3{1}", flagCnt)->disable_flag_override();
|
||||||
|
fApp->add_flag("-e,--flagA", flagAtomic);
|
||||||
|
|
||||||
|
fApp->add_option("-d,--opt1", val8);
|
||||||
|
fApp->add_option("--opt2", val16);
|
||||||
|
fApp->add_option("--opt3", val32);
|
||||||
|
fApp->add_option("--opt4", val64);
|
||||||
|
|
||||||
|
fApp->add_option("--opt5", uval8);
|
||||||
|
fApp->add_option("--opt6", uval16);
|
||||||
|
fApp->add_option("--opt7", uval32);
|
||||||
|
fApp->add_option("--opt8", uval64);
|
||||||
|
|
||||||
|
fApp->add_option("--aopt1", atomicval64);
|
||||||
|
fApp->add_option("--aopt2", atomicuval64);
|
||||||
|
|
||||||
|
fApp->add_option("--dopt1", v1);
|
||||||
|
fApp->add_option("--dopt2", v2);
|
||||||
|
|
||||||
|
fApp->add_option("--vopt1", vv1);
|
||||||
|
fApp->add_option("--vopt2", vvs);
|
||||||
|
fApp->add_option("--vopt3", vstr);
|
||||||
|
fApp->add_option("--vopt4", vecvecd);
|
||||||
|
|
||||||
|
fApp->add_option("--oopt1", od1);
|
||||||
|
fApp->add_option("--oopt2", ods);
|
||||||
|
|
||||||
|
fApp->add_option("--tup1", p1);
|
||||||
|
fApp->add_option("--tup2", t1);
|
||||||
|
fApp->add_option("--tup4", tcomplex);
|
||||||
|
|
||||||
|
fApp->add_option("--dwrap", dwrap);
|
||||||
|
fApp->add_option("--iwrap", iwrap);
|
||||||
|
|
||||||
|
return fApp;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace CLI
|
92
fuzz/fuzzApp.hpp
Normal file
92
fuzz/fuzzApp.hpp
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
// Copyright (c) 2017-2023, University of Cincinnati, developed by Henry Schreiner
|
||||||
|
// under NSF AWARD 1414736 and by the respective contributors.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef CLI11_SINGLE_FILE
|
||||||
|
#include "CLI11.hpp"
|
||||||
|
#else
|
||||||
|
#include "CLI/CLI.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace CLI {
|
||||||
|
|
||||||
|
class intWrapper64 {
|
||||||
|
public:
|
||||||
|
intWrapper64() = default;
|
||||||
|
explicit intWrapper64(int64_t v) : val(v){};
|
||||||
|
CLI11_NODISCARD int64_t value() const { return val; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int64_t val{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
class doubleWrapper {
|
||||||
|
public:
|
||||||
|
doubleWrapper() = default;
|
||||||
|
explicit doubleWrapper(double v) : val(v){};
|
||||||
|
CLI11_NODISCARD double value() const { return val; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
double val{0.0};
|
||||||
|
};
|
||||||
|
|
||||||
|
class FuzzApp {
|
||||||
|
public:
|
||||||
|
FuzzApp() = default;
|
||||||
|
|
||||||
|
std::shared_ptr<CLI::App> generateApp();
|
||||||
|
|
||||||
|
int32_t val32{0};
|
||||||
|
int16_t val16{0};
|
||||||
|
int8_t val8{0};
|
||||||
|
int64_t val64{0};
|
||||||
|
|
||||||
|
uint32_t uval32{0};
|
||||||
|
uint16_t uval16{0};
|
||||||
|
uint8_t uval8{0};
|
||||||
|
uint64_t uval64{0};
|
||||||
|
|
||||||
|
std::atomic<int64_t> atomicval64{0};
|
||||||
|
std::atomic<uint64_t> atomicuval64{0};
|
||||||
|
|
||||||
|
double v1{0};
|
||||||
|
float v2{0};
|
||||||
|
|
||||||
|
std::vector<double> vv1{};
|
||||||
|
std::vector<std::string> vstr{};
|
||||||
|
std::vector<std::vector<double>> vecvecd{};
|
||||||
|
std::vector<std::vector<std::string>> vvs{};
|
||||||
|
std::optional<double> od1{};
|
||||||
|
std::optional<std::string> ods{};
|
||||||
|
std::pair<double, std::string> p1{};
|
||||||
|
std::pair<std::vector<double>, std::string> p2{};
|
||||||
|
std::tuple<int64_t, uint16_t, std::optional<double>> t1{};
|
||||||
|
std::tuple<std::tuple<std::tuple<std::string, double, std::vector<int>>, std::string, double>,
|
||||||
|
std::vector<int>,
|
||||||
|
std::optional<std::string>>
|
||||||
|
tcomplex{};
|
||||||
|
std::tuple<std::tuple<std::tuple<std::string, double, std::vector<int>>, std::string, double>,
|
||||||
|
std::vector<int>,
|
||||||
|
std::optional<std::string>>
|
||||||
|
tcomplex2{};
|
||||||
|
std::string_view vstrv = "";
|
||||||
|
|
||||||
|
bool flag1{false};
|
||||||
|
int flagCnt{0};
|
||||||
|
std::atomic<bool> flagAtomic{false};
|
||||||
|
|
||||||
|
intWrapper64 iwrap{0};
|
||||||
|
doubleWrapper dwrap{0.0};
|
||||||
|
};
|
||||||
|
} // namespace CLI
|
24
fuzz/fuzzCommand.cpp
Normal file
24
fuzz/fuzzCommand.cpp
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// Copyright (c) 2017-2023, University of Cincinnati, developed by Henry Schreiner
|
||||||
|
// under NSF AWARD 1414736 and by the respective contributors.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
#include "fuzzApp.hpp"
|
||||||
|
#include <CLI/CLI.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
|
||||||
|
CLI::FuzzApp fuzzdata;
|
||||||
|
|
||||||
|
auto app = fuzzdata.generateApp();
|
||||||
|
try {
|
||||||
|
|
||||||
|
app->parse(argc, argv);
|
||||||
|
} catch(const CLI::ParseError &e) {
|
||||||
|
(app)->exit(e);
|
||||||
|
// this just indicates we caught an error known by CLI
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
34
fuzz/fuzz_dictionary1.txt
Normal file
34
fuzz/fuzz_dictionary1.txt
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
###### Recommended dictionary.
|
||||||
|
"-a"
|
||||||
|
"-b"
|
||||||
|
"-c"
|
||||||
|
"-d"
|
||||||
|
"-e"
|
||||||
|
"--flag1"
|
||||||
|
"--flag"
|
||||||
|
"--flag2"
|
||||||
|
"--flagA"
|
||||||
|
"--opt1"
|
||||||
|
"--opt2"
|
||||||
|
"--opt3"
|
||||||
|
"--opt4"
|
||||||
|
"--opt5"
|
||||||
|
"--opt6"
|
||||||
|
"--opt7"
|
||||||
|
"--opt8"
|
||||||
|
"--opt9"
|
||||||
|
"--aopt1"
|
||||||
|
"--aopt2"
|
||||||
|
"--dopt1"
|
||||||
|
"--dopt2"
|
||||||
|
"--vopt1"
|
||||||
|
"--vopt2"
|
||||||
|
"--vopt3"
|
||||||
|
"--vopt4"
|
||||||
|
"--oopt1"
|
||||||
|
"--oopt2"
|
||||||
|
"--tup1"
|
||||||
|
"--tup2"
|
||||||
|
"--tup4"
|
||||||
|
"--dwrap"
|
||||||
|
"--iwrap"
|
37
fuzz/fuzz_dictionary2.txt
Normal file
37
fuzz/fuzz_dictionary2.txt
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
###### Recommended dictionary.
|
||||||
|
"a"
|
||||||
|
"b"
|
||||||
|
"c"
|
||||||
|
"d"
|
||||||
|
"e"
|
||||||
|
"flag1"
|
||||||
|
"flag"
|
||||||
|
"flag2"
|
||||||
|
"flagA"
|
||||||
|
"opt1"
|
||||||
|
"opt2"
|
||||||
|
"opt3"
|
||||||
|
"opt4"
|
||||||
|
"opt5"
|
||||||
|
"opt6"
|
||||||
|
"opt7"
|
||||||
|
"opt8"
|
||||||
|
"opt9"
|
||||||
|
"aopt1"
|
||||||
|
"aopt2"
|
||||||
|
"dopt1"
|
||||||
|
"dopt2"
|
||||||
|
"vopt1"
|
||||||
|
"vopt2"
|
||||||
|
"vopt3"
|
||||||
|
"vopt4"
|
||||||
|
"="
|
||||||
|
"oopt1"
|
||||||
|
"oopt2"
|
||||||
|
"tup1"
|
||||||
|
"tup2"
|
||||||
|
"tup4"
|
||||||
|
"tup2"
|
||||||
|
"tup4"
|
||||||
|
"dwrap"
|
||||||
|
"iwrap"
|
@ -1239,9 +1239,17 @@ bool lexical_assign(const std::string &input, AssignTo &output) {
|
|||||||
output = 0;
|
output = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
int val = 0;
|
int val{0};
|
||||||
if(lexical_cast(input, val)) {
|
if(lexical_cast(input, val)) {
|
||||||
|
#if defined(__clang__)
|
||||||
|
/* on some older clang compilers */
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wsign-conversion"
|
||||||
|
#endif
|
||||||
output = val;
|
output = val;
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -1296,11 +1304,13 @@ template <typename AssignTo,
|
|||||||
detail::enabler> = detail::dummy>
|
detail::enabler> = detail::dummy>
|
||||||
bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
|
bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
|
||||||
// the remove const is to handle pair types coming from a container
|
// the remove const is to handle pair types coming from a container
|
||||||
typename std::remove_const<typename std::tuple_element<0, ConvertTo>::type>::type v1;
|
using FirstType = typename std::remove_const<typename std::tuple_element<0, ConvertTo>::type>::type;
|
||||||
typename std::tuple_element<1, ConvertTo>::type v2;
|
using SecondType = typename std::tuple_element<1, ConvertTo>::type;
|
||||||
bool retval = lexical_assign<decltype(v1), decltype(v1)>(strings[0], v1);
|
FirstType v1;
|
||||||
|
SecondType v2;
|
||||||
|
bool retval = lexical_assign<FirstType, FirstType>(strings[0], v1);
|
||||||
if(strings.size() > 1) {
|
if(strings.size() > 1) {
|
||||||
retval = retval && lexical_assign<decltype(v2), decltype(v2)>(strings[1], v2);
|
retval = retval && lexical_assign<SecondType, SecondType>(strings[1], v2);
|
||||||
}
|
}
|
||||||
if(retval) {
|
if(retval) {
|
||||||
output = AssignTo{v1, v2};
|
output = AssignTo{v1, v2};
|
||||||
@ -1460,7 +1470,7 @@ tuple_type_conversion(std::vector<std::string> &strings, AssignTo &output) {
|
|||||||
|
|
||||||
std::size_t index{subtype_count_min<ConvertTo>::value};
|
std::size_t index{subtype_count_min<ConvertTo>::value};
|
||||||
const std::size_t mx_count{subtype_count<ConvertTo>::value};
|
const std::size_t mx_count{subtype_count<ConvertTo>::value};
|
||||||
const std::size_t mx{(std::max)(mx_count, strings.size())};
|
const std::size_t mx{(std::min)(mx_count, strings.size() - 1)};
|
||||||
|
|
||||||
while(index < mx) {
|
while(index < mx) {
|
||||||
if(is_separator(strings[index])) {
|
if(is_separator(strings[index])) {
|
||||||
@ -1470,7 +1480,11 @@ tuple_type_conversion(std::vector<std::string> &strings, AssignTo &output) {
|
|||||||
}
|
}
|
||||||
bool retval = lexical_conversion<AssignTo, ConvertTo>(
|
bool retval = lexical_conversion<AssignTo, ConvertTo>(
|
||||||
std::vector<std::string>(strings.begin(), strings.begin() + static_cast<std::ptrdiff_t>(index)), output);
|
std::vector<std::string>(strings.begin(), strings.begin() + static_cast<std::ptrdiff_t>(index)), output);
|
||||||
|
if(strings.size() > index) {
|
||||||
strings.erase(strings.begin(), strings.begin() + static_cast<std::ptrdiff_t>(index) + 1);
|
strings.erase(strings.begin(), strings.begin() + static_cast<std::ptrdiff_t>(index) + 1);
|
||||||
|
} else {
|
||||||
|
strings.clear();
|
||||||
|
}
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +66,8 @@ target_include_directories(
|
|||||||
CLI11 ${SYSTEM_INCL} ${PUBLIC_OR_INTERFACE} $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
|
CLI11 ${SYSTEM_INCL} ${PUBLIC_OR_INTERFACE} $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
|
||||||
$<INSTALL_INTERFACE:include>)
|
$<INSTALL_INTERFACE:include>)
|
||||||
|
|
||||||
if(CMAKE_VERSION VERSION_LESS 3.8)
|
if(CMAKE_CXX_STANDARD LESS 14)
|
||||||
|
if(CMAKE_VERSION VERSION_LESS 3.8)
|
||||||
# This might not be a complete list
|
# This might not be a complete list
|
||||||
target_compile_features(
|
target_compile_features(
|
||||||
CLI11
|
CLI11
|
||||||
@ -78,8 +79,9 @@ if(CMAKE_VERSION VERSION_LESS 3.8)
|
|||||||
cxx_strong_enums
|
cxx_strong_enums
|
||||||
cxx_constexpr
|
cxx_constexpr
|
||||||
cxx_auto_type)
|
cxx_auto_type)
|
||||||
else()
|
else()
|
||||||
target_compile_features(CLI11 INTERFACE cxx_std_11)
|
target_compile_features(CLI11 INTERFACE cxx_std_11)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CLI11_SINGLE_FILE)
|
if(CLI11_SINGLE_FILE)
|
||||||
|
@ -56,6 +56,10 @@ if(WIN32)
|
|||||||
list(APPEND CLI11_TESTS WindowsTest)
|
list(APPEND CLI11_TESTS WindowsTest)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(CMAKE_CXX_STANDARD GREATER 16)
|
||||||
|
list(APPEND CLI11_TESTS FuzzFailTest)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(Boost_FOUND)
|
if(Boost_FOUND)
|
||||||
list(APPEND CLI11_TESTS BoostOptionTypeTest)
|
list(APPEND CLI11_TESTS BoostOptionTypeTest)
|
||||||
endif()
|
endif()
|
||||||
@ -169,6 +173,16 @@ foreach(T IN LISTS CLI11_MULTIONLY_TESTS)
|
|||||||
add_catch_test(${T})
|
add_catch_test(${T})
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
|
if(CMAKE_CXX_STANDARD GREATER 16)
|
||||||
|
set(TEST_FILE_FOLDER ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
target_compile_definitions(FuzzFailTest PUBLIC -DTEST_FILE_FOLDER="${TEST_FILE_FOLDER}")
|
||||||
|
target_sources(FuzzFailTest PUBLIC ${PROJECT_SOURCE_DIR}/fuzz/fuzzApp.cpp)
|
||||||
|
if(CLI11_SINGLE_FILE AND CLI11_SINGLE_FILE_TESTS)
|
||||||
|
target_compile_definitions(FuzzFailTest_Single PUBLIC -DTEST_FILE_FOLDER="${TEST_FILE_FOLDER}")
|
||||||
|
target_sources(FuzzFailTest_Single PUBLIC ${PROJECT_SOURCE_DIR}/fuzz/fuzzApp.cpp)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
# Add -Wno-deprecated-declarations to DeprecatedTest
|
# Add -Wno-deprecated-declarations to DeprecatedTest
|
||||||
set(no-deprecated-declarations $<$<CXX_COMPILER_ID:MSVC>:/wd4996>
|
set(no-deprecated-declarations $<$<CXX_COMPILER_ID:MSVC>:/wd4996>
|
||||||
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wno-deprecated-declarations>)
|
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wno-deprecated-declarations>)
|
||||||
|
37
tests/FuzzFailTest.cpp
Normal file
37
tests/FuzzFailTest.cpp
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// Copyright (c) 2017-2023, University of Cincinnati, developed by Henry Schreiner
|
||||||
|
// under NSF AWARD 1414736 and by the respective contributors.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
#include "../fuzz/fuzzApp.hpp"
|
||||||
|
#include "app_helper.hpp"
|
||||||
|
|
||||||
|
std::string loadFailureFile(const std::string &type, int index) {
|
||||||
|
std::string fileName(TEST_FILE_FOLDER "/fuzzFail/");
|
||||||
|
fileName.append(type);
|
||||||
|
fileName += std::to_string(index);
|
||||||
|
std::ifstream crashFile(fileName, std::ios::in | std::ios::binary);
|
||||||
|
if(crashFile) {
|
||||||
|
std::vector<char> buffer(std::istreambuf_iterator<char>(crashFile), {});
|
||||||
|
|
||||||
|
std::string cdata(buffer.begin(), buffer.end());
|
||||||
|
return cdata;
|
||||||
|
}
|
||||||
|
return std::string{};
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("app_fail") {
|
||||||
|
CLI::FuzzApp fuzzdata;
|
||||||
|
|
||||||
|
auto app = fuzzdata.generateApp();
|
||||||
|
|
||||||
|
int index = GENERATE(range(1, 3));
|
||||||
|
|
||||||
|
auto parseData = loadFailureFile("fuzz_app_fail", index);
|
||||||
|
try {
|
||||||
|
|
||||||
|
app->parse(parseData);
|
||||||
|
} catch(const CLI::ParseError & /*e*/) {
|
||||||
|
}
|
||||||
|
}
|
@ -305,6 +305,27 @@ template <class X> class objWrapper {
|
|||||||
X val_{};
|
X val_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// simple class to wrap another with a very specific type constructor and assignment operators to test out some of the
|
||||||
|
/// option assignments
|
||||||
|
template <class X> class objWrapperRestricted {
|
||||||
|
public:
|
||||||
|
objWrapperRestricted() = default;
|
||||||
|
explicit objWrapperRestricted(int val) : val_{val} {};
|
||||||
|
objWrapperRestricted(const objWrapperRestricted &) = delete;
|
||||||
|
objWrapperRestricted(objWrapperRestricted &&) = delete;
|
||||||
|
objWrapperRestricted &operator=(const objWrapperRestricted &) = delete;
|
||||||
|
objWrapperRestricted &operator=(objWrapperRestricted &&) = delete;
|
||||||
|
|
||||||
|
objWrapperRestricted &operator=(int val) {
|
||||||
|
val_ = val;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
CLI11_NODISCARD const X &value() const { return val_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
X val_{};
|
||||||
|
};
|
||||||
|
|
||||||
// I think there is a bug with the is_assignable in visual studio 2015 it is fixed in later versions
|
// I think there is a bug with the is_assignable in visual studio 2015 it is fixed in later versions
|
||||||
// so this test will not compile in that compiler
|
// so this test will not compile in that compiler
|
||||||
#if !defined(_MSC_VER) || _MSC_VER >= 1910
|
#if !defined(_MSC_VER) || _MSC_VER >= 1910
|
||||||
@ -347,6 +368,26 @@ TEST_CASE_METHOD(TApp, "doubleWrapper", "[newparse]") {
|
|||||||
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(TApp, "intWrapperRestricted", "[newparse]") {
|
||||||
|
objWrapperRestricted<double> dWrapper;
|
||||||
|
app.add_option("-v", dWrapper);
|
||||||
|
args = {"-v", "4"};
|
||||||
|
|
||||||
|
run();
|
||||||
|
|
||||||
|
CHECK(4.0 == dWrapper.value());
|
||||||
|
|
||||||
|
args = {"-v", "thing"};
|
||||||
|
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
||||||
|
|
||||||
|
args = {"-v", ""};
|
||||||
|
|
||||||
|
run();
|
||||||
|
|
||||||
|
CHECK(0.0 == dWrapper.value());
|
||||||
|
}
|
||||||
|
|
||||||
static_assert(CLI::detail::is_direct_constructible<objWrapper<int>, int>::value,
|
static_assert(CLI::detail::is_direct_constructible<objWrapper<int>, int>::value,
|
||||||
"int wrapper is not constructible from int64");
|
"int wrapper is not constructible from int64");
|
||||||
|
|
||||||
|
@ -336,6 +336,36 @@ TEST_CASE_METHOD(TApp, "pair_check", "[optiontype]") {
|
|||||||
CHECK_THROWS_AS(run(), CLI::ValidationError);
|
CHECK_THROWS_AS(run(), CLI::ValidationError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(TApp, "pair_check_string", "[optiontype]") {
|
||||||
|
std::string myfile{"pair_check_file.txt"};
|
||||||
|
bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
|
||||||
|
CHECK(ok);
|
||||||
|
|
||||||
|
CHECK(CLI::ExistingFile(myfile).empty());
|
||||||
|
std::pair<std::string, std::string> findex;
|
||||||
|
|
||||||
|
auto v0 = CLI::ExistingFile;
|
||||||
|
v0.application_index(0);
|
||||||
|
auto v1 = CLI::PositiveNumber;
|
||||||
|
v1.application_index(1);
|
||||||
|
app.add_option("--file", findex)->check(v0)->check(v1);
|
||||||
|
|
||||||
|
args = {"--file", myfile, "2"};
|
||||||
|
|
||||||
|
CHECK_NOTHROW(run());
|
||||||
|
|
||||||
|
CHECK(myfile == findex.first);
|
||||||
|
CHECK("2" == findex.second);
|
||||||
|
|
||||||
|
args = {"--file", myfile, "-3"};
|
||||||
|
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ValidationError);
|
||||||
|
|
||||||
|
args = {"--file", myfile, "2"};
|
||||||
|
std::remove(myfile.c_str());
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ValidationError);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_METHOD(TApp, "pair_check_take_first", "[optiontype]") {
|
TEST_CASE_METHOD(TApp, "pair_check_take_first", "[optiontype]") {
|
||||||
std::string myfile{"pair_check_file2.txt"};
|
std::string myfile{"pair_check_file2.txt"};
|
||||||
bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
|
bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
|
||||||
|
BIN
tests/fuzzFail/fuzz_app_fail1
Normal file
BIN
tests/fuzzFail/fuzz_app_fail1
Normal file
Binary file not shown.
1
tests/fuzzFail/fuzz_app_fail2
Normal file
1
tests/fuzzFail/fuzz_app_fail2
Normal file
@ -0,0 +1 @@
|
|||||||
|
1 c e g0 g0 æß --tup4 N3CLI10ParseErrorE% 0 %% 0 iÿÿÿÿÿÿÿÿÿÿÿÿÿÿwrap $
|
Loading…
x
Reference in New Issue
Block a user