Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
77ec335
Improve macOS and FreeBSD portability in build and probes
ed-silva-eb Mar 11, 2026
e4ebf21
Add CTest label support and direct-test registration helper
ed-silva-eb Mar 11, 2026
8e7735d
Add targeted regressions for portability behavior
ed-silva-eb Mar 11, 2026
f6ec0c2
Document cross-platform build and label-based test execution
ed-silva-eb Mar 11, 2026
995fe17
Complete CTest label rollout for RPM probe suites
ed-silva-eb Mar 11, 2026
9c2acea
update for macos install and mem tests
ed-silva-eb Mar 11, 2026
0a46f43
Add macOS regression coverage for portability fixes
ed-silva-eb Mar 19, 2026
c58332d
Add shadow offline unsupported regression on macOS
ed-silva-eb Mar 19, 2026
9253a39
Reset stale cached Perl paths during configure
ed-silva-eb Mar 19, 2026
066287b
changes to enable freebsd builds
ed-silva-eb Mar 20, 2026
313542a
updated documentation with macos/freebsd build info
ed-silva-eb Mar 20, 2026
901e250
replace use of strlen()
ed-silva-eb Mar 20, 2026
5d719ba
fix password test issue for freebsd/macos
ed-silva-eb Mar 20, 2026
21ce4d1
remove bogus line
ed-silva-eb Mar 20, 2026
409e4b4
exapnded freebsd testing
ed-silva-eb Mar 20, 2026
953b813
fix freebsd builds
ed-silva-eb Mar 20, 2026
b1f213e
fix freebsd sysctl handling
ed-silva-eb Mar 20, 2026
d24c9d4
fix freebsd sysctl segfault
ed-silva-eb Mar 20, 2026
601cb0f
add freebsd specific memory usage tests
ed-silva-eb Mar 20, 2026
ac907c3
map sed syntax for freebsd instead of assuming we have gsed installed
ed-silva-eb Mar 20, 2026
67a1fa6
fix macos password test and freebsd memory test
ed-silva-eb Mar 23, 2026
033a8bd
keep include directives together
ed-silva-eb Mar 23, 2026
c48a4e4
address sonarqube issues
ed-silva-eb Mar 23, 2026
98f9fa5
address last sonarqube issue
ed-silva-eb Mar 23, 2026
4a91508
Address review feedback on portability fixes
ed-silva-eb Mar 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@ find_package(Ldap)
find_package(OpenDbx)
find_package(PCRE2 REQUIRED)

foreach(_perl_cache_var IN ITEMS PERL_INCLUDE_PATH PERL_LIBRARY)
if(DEFINED ${_perl_cache_var} AND NOT "${${_perl_cache_var}}" STREQUAL "" AND NOT EXISTS "${${_perl_cache_var}}")
message(STATUS "Resetting stale ${_perl_cache_var} cache entry: ${${_perl_cache_var}}")
unset(${_perl_cache_var} CACHE)
endif()
endforeach()

find_package(PerlLibs)
find_package(Popt)
find_package(Systemd)
Expand Down Expand Up @@ -226,7 +233,7 @@ endif()
mark_as_advanced(ENV_PRESENT VALGRIND_PRESENT)
find_program(ENV_PRESENT env)
find_program(VALGRIND_PRESENT valgrind)
find_program(ASCIIDOC_EXECUTABLE asciidoc)
find_program(ASCIIDOC_EXECUTABLE NAMES asciidoc asciidoctor)
find_program(SED_EXECUTABLE sed)
find_program(GIT_EXECUTABLE git)

Expand Down Expand Up @@ -529,8 +536,16 @@ if (MSVC)
endif()

if (${CMAKE_C_COMPILER_ID} STREQUAL "GNU" OR ${CMAKE_C_COMPILER_ID} STREQUAL "Clang")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pipe -W -Wall -Wnonnull -Wshadow -Wformat -Wundef -Wno-unused-parameter -Wmissing-prototypes -Wno-unknown-pragmas -Wno-int-conversion -Werror=implicit-function-declaration -D_GNU_SOURCE -DRBT_IMPLICIT_LOCKING=1 -std=c99")
add_link_options(-Wl,-z,now)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pipe -W -Wall -Wnonnull -Wshadow -Wformat -Wundef -Wno-unused-parameter -Wmissing-prototypes -Wno-unknown-pragmas -Wno-int-conversion -Werror=implicit-function-declaration -DRBT_IMPLICIT_LOCKING=1 -std=c99")
# -D_GNU_SOURCE exposes GNU extensions but changes function signatures on non-glibc
# platforms (e.g. strerror_r on macOS becomes XSI). Only set it on glibc systems.
if(NOT APPLE AND NOT CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GNU_SOURCE")
endif()
# -Wl,-z,now is a Linux/ELF linker flag; not supported on macOS (uses -bind_at_load)
if(NOT APPLE)
add_link_options(-Wl,-z,now)
endif()
endif()
if(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
add_link_options(-lkvm -lm -lprocstat)
Expand Down
17 changes: 17 additions & 0 deletions docs/contribute/testing.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,23 @@ library and then install additional packages required for testing. See the
*Building OpenSCAP on Linux* section in the link:../developer/developer.adoc[OpenSCAP Developer Manual]
for more details.

For platform-focused validation, prefer CTest labels over ad-hoc test lists.
For example, after a successful non-Linux build you can run:

----
$ ctest -L macos
$ ctest -L freebsd
----

For a containerized Linux full-suite run, make sure a local SMTP listener and a
session D-Bus are available before invoking CTest, otherwise MITRE, `fwupd`,
and `systemd` coverage may be skipped or fail for environmental reasons:

----
$ postfix start
$ dbus-run-session -- ctest --output-on-failure
----


== Writing a new test
In this guide we will use an example to describe the process of writing a test
Expand Down
93 changes: 92 additions & 1 deletion docs/developer/developer.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ $ cmake ../
$ make
----

If you reuse an existing build tree after a Perl upgrade or package-manager
change, CMake may retain stale `PERL_INCLUDE_PATH` or `PERL_LIBRARY` cache
entries. The top-level build now clears cached Perl paths that no longer exist,
so rerunning `cmake ../` in the same build directory is usually sufficient.
If Perl detection still looks wrong, remove `CMakeCache.txt` and reconfigure.

On Ubuntu 18.04 and potentially other distro, the python3 dist-packages path is wrong.
If the following command:

Expand Down Expand Up @@ -168,6 +174,43 @@ Now you can execute the following command to run library self-checks:
$ ctest
----

For containerized Linux validation, start a local MTA and provide a session
D-Bus before invoking the full suite so MITRE, `fwupd`, and `systemd`-related
coverage stays active:

----
$ postfix start
$ dbus-run-session -- ctest --output-on-failure
----

The test suite supports filtering by labels. This is useful for platform-specific
or subsystem-specific runs:

----
$ ctest -L probes
$ ctest -L api
$ ctest -L unix
$ ctest -L independent
$ ctest -L linux_only
$ ctest -L macos
$ ctest -L freebsd
----

Labels are assigned in `tests/CMakeLists.txt` by helper functions:

* `add_oscap_test(script.sh [LABELS ...])`:
** registers shell-based tests,
** always adds `shell`,
** automatically adds a suite label based on the top-level test path
(`api`, `probes`, `report`, `sources`, etc.),
** appends optional explicit labels (for example `unix`, `linux_only`, `macos`).

* `add_oscap_ctest(name COMMAND ... [LABELS ...])`:
** registers direct CTest commands (for example Python/pytest tests),
** always adds `ctest`,
** automatically adds the same top-level suite label,
** appends optional explicit labels.

Note that using the `--jobs/-j` flag is currently not supported.
It will cause unexpected test failures.
See link:https://github.com/OpenSCAP/openscap/issues/2057[#2057] for more details.
Expand Down Expand Up @@ -208,6 +251,55 @@ $ docker build --tag openscap_mitre_tests:latest -f Dockerfiles/mitre_tests . &&

--

== Building on macOS and FreeBSD

OpenSCAP can be built on macOS and FreeBSD with a reduced feature set depending
on available libraries and enabled probes.

Typical configuration starts with:

----
$ mkdir -p build && cd build
$ cmake .. -DENABLE_TESTS=ON -DENABLE_PROBES_LINUX=OFF
$ make
----

Notes:

* Linux-specific probes (`ENABLE_PROBES_LINUX`) should be disabled on non-Linux
systems unless you are explicitly cross-compiling for Linux.
* Some tests are intentionally labeled `linux_only` and should be filtered out
using CTest labels.
* After a successful non-Linux build, `ctest -L macos` or `ctest -L freebsd`
provides a quick portability smoke test without pulling in Linux-only cases.
* On macOS, `SCE` is disabled by default in the main CMake configuration.

=== Recent portability updates

The codebase contains recent portability work for macOS/FreeBSD, including:

* `sysctl` probe support for macOS (`/usr/sbin/sysctl -ae`) and FreeBSD/macOS
branching, including parsing of multiline BSD `sysctl -ae` values by treating
only valid `name=value` headers as new items,
* `memusage` support on macOS via Mach APIs,
* `XCCDF` target MAC collection on macOS via `AF_LINK`,
* fallback parser for password probe offline mode on systems without
`fgetpwent(3)`,
* shadow probe offline mode explicitly marked unsupported on platforms where
the Linux-style shadow path does not apply,
* runlevel probe behavior explicitly marked unsupported on macOS/FreeBSD
(SysV runlevels are Linux/Solaris specific).

Targeted regression tests for these portability areas are located in:

* `tests/API/XCCDF/unittests/test_xccdf_result_sysinfo_platform.sh`
* `tests/API/probes/test_memusage_platform.sh`
* `tests/probes/password/test_probes_password_offline_fallback.sh`
* `tests/probes/runlevel/test_probes_runlevel_unsupported.sh`
* `tests/probes/shadow/test_probes_shadow_offline_unsupported.sh`
* `tests/probes/sysctl/test_sysctl_probe.sh`
* `tests/probes/sysctl/test_sysctl_probe_all.sh`

. *Install*
+
--
Expand Down Expand Up @@ -369,4 +461,3 @@ For more information about OpenSCAP library, you can refer to this online
reference manual: http://static.open-scap.org/openscap-1.2/[OpenSCAP
reference manual]. This manual is included in a release tarball and can be
regenerated from project sources by Doxygen documentation system.

2 changes: 2 additions & 0 deletions src/OVAL/probes/unix/interface_probe.c
Original file line number Diff line number Diff line change
Expand Up @@ -298,8 +298,10 @@ static void get_flags(const struct ifaddrs *ifa, char ***fp) {
if (ifa->ifa_flags & IFF_RENAMING)
flags_buf[i++] = "RENAMING";

#ifdef IFF_NOGROUP
if (ifa->ifa_flags & IFF_NOGROUP)
flags_buf[i++] = "NOGROUP";
#endif
}

flags_buf[i] = NULL;
Expand Down
74 changes: 74 additions & 0 deletions src/OVAL/probes/unix/password_probe.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <inttypes.h>
#include <pwd.h>
#include <paths.h>
#if defined(OS_APPLE)
Expand All @@ -67,6 +68,79 @@
#include <probe/option.h>
#include "password_probe.h"

/*
* fgetpwent() is a GNU/glibc extension; provide a portable fallback.
* This parses a standard /etc/passwd-format file one entry at a time.
*/
#if defined(OS_APPLE) || defined(OS_FREEBSD)
static int oscap_parse_passwd_id(const char *field, uintmax_t max_value,
uintmax_t *parsed_value)
{
char *endptr;
uintmax_t value;

errno = 0;
value = strtoumax(field, &endptr, 10);
if (errno != 0 || endptr == field || *endptr != '\0' || value > max_value)
return -1;

*parsed_value = value;
return 0;
}

static struct passwd *oscap_fgetpwent(FILE *fp)
{
static char line[2048];
static struct passwd pw;
char *fields[7], *newline, *line_end, *p;
uintmax_t parsed_uid, parsed_gid;
int f;

while (fgets(line, sizeof(line), fp) != NULL) {
newline = memchr(line, '\n', sizeof(line) - 1);
line_end = memchr(line, '\0', sizeof(line));
if (newline != NULL)
*newline = '\0';
else if (line_end == &line[sizeof(line) - 1]) {
int ch;

/* Skip truncated records instead of parsing partial passwd entries. */
while ((ch = fgetc(fp)) != '\n' && ch != EOF)
;
continue;
}

if (line[0] == '#')
continue;
f = 0;
p = line;
while (f < 7) {
fields[f++] = p;
p = strchr(p, ':');
if (p)
*p++ = '\0';
else
break;
}
if (f < 7)
continue;
if (oscap_parse_passwd_id(fields[2], (uintmax_t)(uid_t)-1, &parsed_uid) != 0 ||
oscap_parse_passwd_id(fields[3], (uintmax_t)(gid_t)-1, &parsed_gid) != 0)
continue;
pw.pw_name = fields[0];
pw.pw_passwd = fields[1];
pw.pw_uid = (uid_t)parsed_uid;
pw.pw_gid = (gid_t)parsed_gid;
pw.pw_gecos = fields[4];
pw.pw_dir = fields[5];
pw.pw_shell = fields[6];
return &pw;
}
return NULL;
}
#define fgetpwent oscap_fgetpwent
#endif

/* Convenience structure for the results being reported */
struct result_info {
const char *username;
Expand Down
3 changes: 2 additions & 1 deletion src/OVAL/probes/unix/runlevel_probe.c
Original file line number Diff line number Diff line change
Expand Up @@ -468,9 +468,10 @@ static int get_runlevel (struct runlevel_req *req, struct runlevel_rep **rep)
_A(rep != NULL);
return GET_RUNLEVEL(LINUX_DISTRO, req, rep);
}
#elif defined(OS_FREEBSD)
#elif defined(OS_FREEBSD) || defined(OS_APPLE)
static int get_runlevel (struct runlevel_req *req, struct runlevel_rep **rep)
{
/* SysV runlevels are a Linux/Solaris concept; not applicable on BSD/macOS */
_A(req != NULL);
_A(rep != NULL);
return (-1);
Expand Down
5 changes: 5 additions & 0 deletions src/OVAL/probes/unix/shadow_probe.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@
#include "shadow_probe.h"

#ifndef HAVE_SHADOW_H
int shadow_probe_offline_mode_supported()
{
return PROBE_OFFLINE_NONE;
}

int shadow_probe_main(probe_ctx *ctx, void *arg)
{
SEXP_t *item_sexp;
Expand Down
Loading