diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7cba398a..b41694b9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -19,7 +19,7 @@ cmake_minimum_required(VERSION 3.15)
project(csdiff CXX)
enable_testing()
-# C/C++ sources
+# source code
add_subdirectory(src)
# regression tests
diff --git a/make-srpm.sh b/make-srpm.sh
index 1a696c67..0363a768 100755
--- a/make-srpm.sh
+++ b/make-srpm.sh
@@ -202,6 +202,7 @@ make version.cc
%doc README
%license COPYING
%{_bindir}/csdiff
+%{_bindir}/csfilter-kfp
%{_bindir}/csgrep
%{_bindir}/cshtml
%{_bindir}/cslinker
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e8ea9c18..174de791 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -82,6 +82,11 @@ install(TARGETS
cstrans-df-run
DESTINATION ${CMAKE_INSTALL_BINDIR})
+# install the csfilter-kfp script
+install(PROGRAMS
+ csfilter-kfp
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+
# optionally build statically linked csgrep-static
option(CSGREP_STATIC "Set to ON to build the csgrep-static executable" OFF)
if(CSGREP_STATIC)
diff --git a/src/csfilter-kfp b/src/csfilter-kfp
new file mode 100755
index 00000000..1a85813c
--- /dev/null
+++ b/src/csfilter-kfp
@@ -0,0 +1,125 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2024 Red Hat, Inc.
+#
+# This file is part of csdiff.
+#
+# csdiff is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# any later version.
+#
+# csdiff is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with csdiff. If not, see .
+
+import argparse
+import subprocess
+import sys
+
+
+def construct_filter_cmd(args):
+ cmd = ""
+ if args.verbose:
+ # run shell in XTRACE mode
+ cmd += "set -x\n"
+
+ # TODO
+ # if not props.known_false_positives:
+ # return
+
+ # # update scan metadata
+ # results.ini_writer.append("known-false-positives", props.known_false_positives)
+ # cmd = ["rpm", "-qf", props.known_false_positives]
+ # (ec, out) = results.get_cmd_output(cmd, shell=False)
+ # if 0 == ec:
+ # # record the RPM package that provided the known-false-positives file
+ # results.ini_writer.append("known-false-positives-rpm", out.strip())
+
+ # # install global filter of known false positives
+ # filter_cmd = f'csdiff --json-output --show-internal "{props.known_false_positives}" -'
+ # props.result_filters += [filter_cmd]
+
+ # if props.pkg is None:
+ # # no package name available
+ # return
+
+ # kfp_dir = re.sub("\\.js", ".d", props.known_false_positives)
+ # if not os.path.isdir(kfp_dir):
+ # # no per-pkg known false positives available
+ # return
+
+ # ep_file = os.path.join(kfp_dir, props.pkg, "exclude-paths.txt")
+ # if not os.path.exists(ep_file):
+ # # no list of path regexes to exclude for this pkg
+ # return
+
+ # # install path exclusion filters for this pkg
+ # with open(ep_file) as file_handle:
+ # lines = file_handle.readlines()
+ # for line in lines:
+ # path_re = line.strip()
+ # if len(path_re) == 0 or path_re.startswith("#"):
+ # # skip comments and empty lines
+ # continue
+ # filter_cmd = f'csgrep --mode=json --invert-match --path={shlex.quote(path_re)}'
+ # props.result_filters += [filter_cmd]
+
+ # TODO: implement
+ assert args.kfp_dir is None
+ assert args.kfp_git_url is None
+ assert args.record_excluded is None
+ cmd += "echo Please implement!\n"
+ return cmd
+
+
+def main():
+ # initialize argument parser
+ parser = argparse.ArgumentParser()
+
+ parser.add_argument(
+ "input-file", nargs="?",
+ help="optional name of the input file (standard input is used by default)")
+
+ # source of known-false-positives
+ kfp_source = parser.add_mutually_exclusive_group()
+ kfp_source.add_argument(
+ "--kfp-dir",
+ help="known false positives file")
+ kfp_source.add_argument(
+ "--kfp-git-url",
+ help="known false positives git URL (optionally taking a pinned revision delimited by #)")
+
+ # TODO:--proj-name/--proj-nvr
+ # cut off the `-version-release` or `-version` suffix to obtain package name where `version` can be
+ # a number optionally prefixed by `v` or a full-size SHA1 hash encoded in lowercase as, for example,
+ # in `project-koku-koku-cbe5e5c3355c1e140aa1cca7377aebe09d8d8466`
+ # name = re.sub("-(([v]?[0-9][^-]*)|([0-9a-f]{40}))(-[0-9][^-]*)?$", "", nvr)
+
+ parser.add_argument(
+ "--record-excluded",
+ help="file to store all excluded findings to")
+
+ parser.add_argument(
+ "-v", "--verbose", action="store_true",
+ help="run shell in XTRACE mode while executing the filtering script")
+
+ # parse command-line arguments
+ args = parser.parse_args()
+
+ # construct the command to filter
+ cmd = construct_filter_cmd(args)
+
+ # run the command
+ try:
+ subprocess.run(cmd, shell=True, check=True)
+ except subprocess.CalledProcessError as e:
+ sys.exit(e.returncode)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 41a12f9e..e68a5447 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -29,8 +29,8 @@ set(jsfilter "sed -e 's|\"version\": \"[^\"]*\"|\"version\": \"\"|g'")
macro(add_test_wrap test_name cmd)
add_test("${test_name}" bash -c "${cmd}")
- set_tests_properties(${test_name} PROPERTIES
- ENVIRONMENT "PROJECT_ROOT=${CMAKE_SOURCE_DIR}")
+ set_tests_properties(${test_name} PROPERTIES ENVIRONMENT
+ "PATH=${CMAKE_BINARY_DIR}/src:$ENV{PATH};PROJECT_ROOT=${CMAKE_SOURCE_DIR}")
set_tests_properties(${test_name} PROPERTIES COST ${test_cost})
math(EXPR test_cost "${test_cost} - 1")
@@ -45,6 +45,7 @@ endmacro()
set(test_cost 1048576)
add_subdirectory(csdiff)
+add_subdirectory(csfilter-kfp)
add_subdirectory(csgrep)
add_subdirectory(cshtml)
add_subdirectory(cslinker)
diff --git a/tests/csfilter-kfp/0001-args.txt b/tests/csfilter-kfp/0001-args.txt
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/csfilter-kfp/0001-stdout.txt b/tests/csfilter-kfp/0001-stdout.txt
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/csfilter-kfp/CMakeLists.txt b/tests/csfilter-kfp/CMakeLists.txt
new file mode 100644
index 00000000..cd952334
--- /dev/null
+++ b/tests/csfilter-kfp/CMakeLists.txt
@@ -0,0 +1,30 @@
+# Copyright (C) 2024 Red Hat, Inc.
+#
+# This file is part of csdiff.
+#
+# csdiff is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# any later version.
+#
+# csdiff is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with csdiff. If not, see .
+
+# a generic template for cstrans-df-run tests
+macro(test_csfilter_kfp tst)
+ set(test_data_prefix "${CMAKE_CURRENT_SOURCE_DIR}/${tst}")
+ set(cmd "${CMAKE_SOURCE_DIR}/src/csfilter-kfp")
+ file(READ ${test_data_prefix}-args.txt args)
+ string(REPLACE "\n" "" args "${args}")
+ set(cmd "${cmd} ${args} <${test_data_prefix}-stdin.txt")
+ set(cmd "${cmd} | ${diffcmd} ${test_data_prefix}-stdout.txt -")
+ add_test_wrap("csfilter-kfp-${tst}" "${cmd}")
+endmacro()
+
+# csfilter-kpf tests
+test_csfilter_kfp(0001)