diff --git a/README.md b/README.md index 9c33e34a..22aa46c6 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ You can include these artifacts in your Maven POM as a dependency. ## Building -This project can be built with [Apache Maven](https://maven.apache.org/) version 3.8.4 or greater. +This project can be built with [Apache Maven](https://maven.apache.org/) version 3.9.0 or greater. The following instructions can be used to clone and build this project. diff --git a/src/site/markdown/building.md.vm b/src/site/markdown/building.md.vm new file mode 100644 index 00000000..a0bc58ad --- /dev/null +++ b/src/site/markdown/building.md.vm @@ -0,0 +1,192 @@ +# Building from Source + +This guide explains how to build ${project.name} from source code. + +## Prerequisites + +| Requirement | Minimum Version | Notes | +|:------------|:----------------|:------| +| Java JDK | 17 | Required for building (output JARs are JDK 11 compatible) | +| Maven | 3.9.0 | Required for building | +| Git | 2.0 | Required for cloning | + +Ensure `JAVA_HOME` is set correctly: + +```bash +# Linux/macOS +export JAVA_HOME=$(/usr/libexec/java_home -v 17) + +# Windows +set JAVA_HOME=C:\Program Files\Java\jdk-17 +``` + +## Clone the Repository + +Clone the repository with submodules: + +```bash +git clone --recurse-submodules https://github.com/metaschema-framework/liboscal-java.git +cd liboscal-java +``` + +If you already cloned without submodules, initialize them: + +```bash +git submodule update --init --recursive +``` + +## Build Commands + +### Basic Build + +Build and install to your local Maven repository: + +```bash +mvn install +``` + +### Run Tests Only + +```bash +mvn test +``` + +### Run a Single Test + +```bash +# Run a single test class +mvn test -Dtest=ExamplesTest + +# Run a single test method +mvn test -Dtest=ExamplesTest#testExample +``` + +### Skip Tests + +```bash +mvn install -DskipTests +``` + +### CI/CD Build + +Replicate the full CI/CD build (recommended before pushing): + +```bash +mvn install -PCI -Prelease +``` + +This enables additional checks including: +- License header verification +- Code style checks +- Full test suite + +## Code Quality Commands + +### Check License Headers + +```bash +mvn license:check +``` + +### Auto-format Source Code + +```bash +mvn formatter:format +``` + +### Check for Checkstyle Issues + +```bash +mvn checkstyle:check +``` + +## Common Build Issues + +### Submodule Not Initialized + +**Symptom:** Build fails with missing OSCAL schema files. + +**Solution:** Initialize submodules: + +```bash +git submodule update --init --recursive +``` + +### Java Version Mismatch + +**Symptom:** Compilation errors or "unsupported class file version" errors. + +**Solution:** Ensure you're using Java 17 or later for building (required due to build plugin requirements): + +```bash +java -version +mvn -version +``` + +### Maven Version Too Old + +**Symptom:** Build fails with plugin compatibility errors. + +**Solution:** Upgrade Maven to 3.9.0 or later. Check your version: + +```bash +mvn -version +``` + +### Out of Memory + +**Symptom:** Build fails with `OutOfMemoryError`. + +**Solution:** Increase Maven's heap size: + +```bash +export MAVEN_OPTS="-Xmx2g" +mvn install +``` + +### License Header Check Fails + +**Symptom:** Build fails during `license:check` phase. + +**Solution:** Run the formatter to add missing headers: + +```bash +mvn license:format +``` + +## Building the Site + +To build the project documentation site: + +```bash +mvn install site -DskipTests +``` + +**Note:** The `install` and `site` goals must run together in a single Maven invocation. This is required because the Javadoc plugin needs access to the generated OSCAL model classes, which are only available after the `install` phase completes within the same Maven session. + +Running `mvn site` alone will fail with Javadoc errors about missing model classes. + +## Generated Sources + +OSCAL model classes are generated during the build from Metaschema definitions in the `oscal/` submodule. These generated classes appear in: + +``` +target/generated-sources/metaschema/ +``` + +If you make changes to the OSCAL Metaschema modules, run a clean build: + +```bash +mvn clean install +``` + +## Contributing + +For contribution guidelines, including code style requirements and the pull request process, see [CONTRIBUTING.md](https://github.com/metaschema-framework/liboscal-java/blob/develop/CONTRIBUTING.md). + +## Next Steps + +Once you've built the project, explore these resources to start using the library: + +- [Installation](installation.html) - Add the library to your project +- [Architecture](guides/architecture.html) - Understand the library structure diff --git a/src/site/markdown/claude-integration.md.vm b/src/site/markdown/claude-integration.md.vm new file mode 100644 index 00000000..d91c9cb4 --- /dev/null +++ b/src/site/markdown/claude-integration.md.vm @@ -0,0 +1,139 @@ +# Claude Code Integration + +${project.name} includes configuration for [Claude Code](https://docs.anthropic.com/en/docs/claude-code), Anthropic's agentic coding tool. This integration enables AI-assisted development with context-aware guidance specific to OSCAL and this library. + +## Overview + +Claude Code can assist with: + +- Understanding the OSCAL data model and library architecture +- Writing code that uses ${project.name} APIs +- Debugging issues with OSCAL document processing +- Implementing profile resolution and validation workflows +- Following project coding conventions and best practices + +## Project Configuration + +This repository includes a `.claude/` directory with project-specific configuration: + +- **CLAUDE.md** - Project instructions and conventions that Claude follows +- **settings.json** - Tool permissions and project settings + +The configuration provides Claude with: + +- Build and test commands specific to this project +- Code style and architecture guidelines +- Git workflow requirements (worktrees, PR process) +- Links to relevant documentation + +## Claude Plugins + +Enhanced capabilities are available through [Claude plugins](https://github.com/metaschema-framework/claude-plugins/tree/main) from the metaschema-framework organization: + +### OSCAL Plugin + +The `oscal` plugin provides OSCAL-specific skills: + +| Skill | Description | +|:------|:------------| +| `oscal:oscal-basics` | OSCAL document structure, models, and concepts | + +### Metaschema Plugin + +The `metaschema` plugin provides Metaschema knowledge: + +| Skill | Description | +|:------|:------------| +| `metaschema:metaschema-basics` | Introduction to Metaschema concepts | +| `metaschema:metaschema-module-authoring` | Creating and modifying Metaschema modules | +| `metaschema:metaschema-constraints-authoring` | Writing validation constraints | +| `metaschema:metapath-expressions` | Metapath query language syntax | + +### Metaschema Tools Plugin + +The `metaschema-tools` plugin provides CLI and library guidance: + +| Skill | Description | +|:------|:------------| +| `metaschema-tools:metaschema-java-library` | Java library interfaces and patterns | +| `metaschema-tools:using-metaschema-java` | CLI commands for validation and conversion | + +### OSCAL Tools Plugin + +The `oscal-tools` plugin provides CLI guidance: + +| Skill | Description | +|:------|:------------| +| `oscal-tools:using-oscal-cli` | OSCAL CLI commands and workflows | + +## Installing Plugins + +To install metaschema-framework plugins, use the Claude Code settings or install via command line: + +```bash +# Install individual plugins +claude plugins:install metaschema-framework/oscal +claude plugins:install metaschema-framework/metaschema +claude plugins:install metaschema-framework/metaschema-tools +claude plugins:install metaschema-framework/oscal-tools +``` + +## Example Workflows + +### Understanding the Library + +Ask Claude to explain library concepts: + +``` +"Explain how OscalBindingContext works and when I would use it" +"What's the difference between reading OSCAL as XML vs JSON?" +"How does profile resolution work in this library?" +``` + +### Writing Code + +Ask Claude to help write code: + +``` +"Write code to load an OSCAL catalog from a JSON file" +"Create a method that validates an SSP against its imported profile" +"Show me how to programmatically create a component definition" +``` + +### Debugging + +Ask Claude to help debug issues: + +``` +"This profile resolution is failing with [error]. What's wrong?" +"Why am I getting a validation error on this control?" +"Help me understand this Metapath constraint violation" +``` + +### Following Best Practices + +Ask Claude about conventions: + +``` +"What's the correct way to handle null values in this library?" +"How should I structure tests for OSCAL document processing?" +"What naming conventions does this project use?" +``` + +## Development Workflow Integration + +Claude Code integrates with the development workflow defined in CLAUDE.md: + +1. **Git Worktrees** - Claude will guide you to use worktrees for feature development +2. **PR Process** - Claude knows to push to your fork and target the develop branch +3. **Testing** - Claude will run tests before suggesting code is complete +4. **Code Style** - Claude applies the project's formatting and style conventions + +## Related Resources + +For more information about Claude Code and the technologies used in this project: + +- [Claude Code Documentation](https://docs.anthropic.com/en/docs/claude-code) +- [Metaschema Framework Plugins](https://github.com/metaschema-framework/claude-plugins/tree/main) +- [OSCAL Documentation](https://pages.nist.gov/OSCAL/) +- [Metaschema Specification](https://metaschema.dev/) diff --git a/src/site/markdown/guides/architecture.md.vm b/src/site/markdown/guides/architecture.md.vm new file mode 100644 index 00000000..8108d023 --- /dev/null +++ b/src/site/markdown/guides/architecture.md.vm @@ -0,0 +1,294 @@ +# Architecture + +This guide explains the architecture and module structure of liboscal-java. + +## Overview + +liboscal-java is built on top of the [Metaschema Java Tools](https://github.com/metaschema-framework/metaschema-java) framework. It provides OSCAL-specific functionality while leveraging the general-purpose Metaschema capabilities for serialization, validation, and querying. + +Understanding the architecture helps you work effectively with the library, especially when debugging issues or extending its capabilities. The library follows a layered design where each layer builds on the capabilities provided by the layer below. + +## Library Structure + +This section describes how the library is organized and relates to its dependencies. + +### Dependency Hierarchy + +The following diagram shows how liboscal-java relates to the underlying Metaschema framework and the OSCAL model definitions: + +``` +liboscal-java + │ + ├── metaschema-databind (data binding, serialization) + │ ├── metaschema-core (Metaschema model, Metapath) + │ └── metaschema-model (module loading) + │ + └── OSCAL Metaschema modules (generated model classes) + ├── oscal_catalog + ├── oscal_profile + ├── oscal_mapping + ├── oscal_ssp + ├── oscal_component-definition + ├── oscal_assessment-plan + ├── oscal_assessment-results + └── oscal_poam +``` + +### Package Structure + +The library's Java packages are organized by functionality. The main entry point is `OscalBindingContext`, and all OSCAL model classes live under the `model` subpackage: + +``` +dev.metaschema.oscal.lib +├── OscalBindingContext # Central entry point +├── model/ # Generated OSCAL model classes +│ ├── Catalog +│ ├── Profile +│ ├── MappingCollection +│ ├── SystemSecurityPlan +│ ├── ComponentDefinition +│ ├── AssessmentPlan +│ ├── AssessmentResults +│ └── PlanOfActionAndMilestones +├── profile/ +│ └── resolver/ # Profile resolution +│ └── ProfileResolver +└── metapath/ + └── function/ + └── library/ # OSCAL-specific Metapath functions + └── OscalFunctionLibrary +``` + +## Key Components + +The following sections describe the main classes you'll interact with when using the library. + +### OscalBindingContext + +The `OscalBindingContext` is your starting point for all OSCAL operations. It provides factory methods for creating serializers, deserializers, and validators: + +```java +OscalBindingContext context = OscalBindingContext.instance(); + +// Create readers/writers +IDeserializer reader = context.newDeserializer(Format.JSON, Catalog.class); +ISerializer writer = context.newSerializer(Format.JSON, Catalog.class); + +// Access Metapath support +StaticContext staticCtx = context.getStaticContext(); +``` + +**Responsibilities:** +- Load OSCAL Metaschema modules +- Register OSCAL-specific Metapath functions +- Provide serialization/deserialization factories +- Manage constraint validation + +### Generated Model Classes + +Rather than hand-writing Java classes for each OSCAL element, the library generates them from the official OSCAL Metaschema definitions. This ensures the Java model always matches the OSCAL specification: + +``` +oscal/src/main/metaschema/ + ├── oscal_catalog_metaschema.xml + ├── oscal_profile_metaschema.xml + └── ... + ↓ (maven-metaschema-plugin) +target/generated-sources/metaschema/ + └── dev/metaschema/oscal/lib/model/ + ├── Catalog.java + ├── Profile.java + └── ... +``` + +### ProfileResolver + +One of the most important OSCAL operations is profile resolution—converting a profile (which references controls in external catalogs) into a standalone resolved catalog. The `ProfileResolver` class handles this: + +```java +ProfileResolver resolver = new ProfileResolver(); +Catalog resolved = resolver.resolve(profile); +``` + +**Resolution steps:** +1. Load imported catalogs/profiles +2. Apply control selections +3. Apply modifications +4. Merge controls from multiple imports +5. Generate resolved catalog + +### OSCAL Function Library + +The library extends the base Metapath expression language with OSCAL-specific functions. These are automatically registered when you use `OscalBindingContext`: + +| Function | Purpose | +|:---------|:--------| +| `has-oscal-namespace` | Check namespace membership | +| `resolve-profile` | Resolve profile imports | +| `resolve-reference` | Resolve internal references | + +## Data Flow + +Understanding how data flows through the system helps when debugging issues or optimizing performance. This section traces the path of data through the major operations. + +### Reading Documents + +When you deserialize an OSCAL document, the library detects the format, parses the content, and maps it to Java objects: + +``` +File/URL + ↓ +Format Detection (XML/JSON/YAML) + ↓ +Parser (Jackson/StAX) + ↓ +Metaschema Databind (unmarshalling) + ↓ +Generated Model Objects + ↓ +Optional: Constraint Validation + ↓ +Application Code +``` + +### Writing Documents + +Serialization is the reverse process—converting your Java objects back to XML, JSON, or YAML: + +``` +Model Objects + ↓ +Metaschema Databind (marshalling) + ↓ +Serializer (Jackson/StAX) + ↓ +Format Output (XML/JSON/YAML) + ↓ +File/Stream +``` + +### Profile Resolution + +Profile resolution is more complex because it may involve loading external resources and applying multiple transformations: + +``` +Profile (with imports) + ↓ +Load Referenced Catalogs/Profiles (recursive) + ↓ +Apply Selections (include/exclude) + ↓ +Apply Modifications (parameters, alterations) + ↓ +Merge Controls + ↓ +Resolved Catalog +``` + +## Integration and Extension + +This section describes how the library integrates with the Metaschema ecosystem and how you can extend it. + +### Metaschema Framework Integration + +liboscal-java extends the base Metaschema framework with OSCAL-specific functionality. The following table shows how the library maps to Metaschema components: + +| Metaschema Component | liboscal-java Usage | +|:---------------------|:--------------------| +| `IBindingContext` | `OscalBindingContext` extends it | +| `IDeserializer` | Load OSCAL documents | +| `ISerializer` | Write OSCAL documents | +| `MetapathExpression` | Query OSCAL content | +| `IConstraintValidationHandler` | Validate constraints | + +### Extension Points + +The library provides hooks for adding custom functionality without modifying the core code. + +#### Custom Metapath Functions + +You can extend the Metapath query language with domain-specific functions. This is useful when you need operations that aren't provided by the built-in function library: + +```java +// Register custom function +context.registerFunction(myCustomFunction); +``` + +#### Custom Constraint Handlers + +To customize how constraint violations are reported or handled, implement your own validation handler. This allows integration with logging frameworks, custom error reporting, or workflow systems: + +```java +IConstraintValidationHandler handler = new MyHandler(); +deserializer.setConstraintValidationHandler(handler); +``` + +## Runtime Considerations + +This section covers topics important when deploying applications that use the library. + +### Threading Model + +When using the library in multi-threaded applications, understanding which components are thread-safe helps avoid subtle bugs. The general pattern is to share the context but create fresh serializers and deserializers for each operation: + +- `OscalBindingContext.instance()` is thread-safe for reading configuration +- Deserializers are single-use (create new per operation) +- Serializers are single-use (create new per operation) +- Model objects are not thread-safe for concurrent modification + +The following example shows the recommended pattern for parallel processing: + +```java +// Good: Shared context, per-operation deserializers +OscalBindingContext context = OscalBindingContext.instance(); + +executor.submit(() -> { + IDeserializer reader = context.newDeserializer(Format.JSON, Catalog.class); + Catalog catalog = reader.deserialize(path); + // process catalog +}); +``` + +### Memory Considerations + +OSCAL documents can vary significantly in size, from small component definitions to comprehensive catalogs like NIST SP 800-53. Keep these factors in mind when working with larger documents: + +- Large catalogs (SP 800-53) can consume significant memory when fully loaded +- Consider streaming approaches for very large documents if you only need to process portions +- Profile resolution loads the entire import chain into memory, which can be substantial for profiles with deep import hierarchies + +## Build Process + +The library uses Maven for its build process. A key step is the automatic generation of Java model classes from the OSCAL Metaschema definitions. This ensures the Java API always matches the official OSCAL specification: + +``` +1. Compile Metaschema Sources + ↓ +2. Generate Java Model Classes (maven-metaschema-plugin) + ↓ +3. Compile Generated + Handwritten Java + ↓ +4. Run Tests + ↓ +5. Package JAR +``` + +If you're building from source, the generated classes appear in `target/generated-sources/metaschema/` and are automatically included in the compilation. + +## Related Projects + +liboscal-java is part of a larger ecosystem of tools for working with OSCAL and Metaschema. Understanding these relationships helps when you need to trace issues or find additional capabilities: + +| Project | Relationship | +|:--------|:-------------| +| [metaschema-java](https://github.com/metaschema-framework/metaschema-java) | Core framework dependency | +| [oscal-cli](https://github.com/metaschema-framework/oscal-cli) | CLI built on liboscal-java | +| [OSCAL](https://github.com/usnistgov/OSCAL) | Source Metaschema definitions | + +## Next Steps + +Continue learning about liboscal-java with these related guides: + +- [Loading OSCAL Modules](loading-oscal-modules.html) - Get started with the context +- [Reading & Writing Data](reading-writing-data.html) - Work with documents +- [Installation](../installation.html) - Add to your project diff --git a/src/site/markdown/guides/executing-metapath.md.vm b/src/site/markdown/guides/executing-metapath.md.vm new file mode 100644 index 00000000..af0b3de4 --- /dev/null +++ b/src/site/markdown/guides/executing-metapath.md.vm @@ -0,0 +1,276 @@ +# Executing Metapath + +This guide explains how to use Metapath expressions to query OSCAL documents programmatically. + +## Overview + +Metapath is an expression language for querying Metaschema-based documents. It's similar to XPath but works across all formats (XML, JSON, YAML). + +## Basic Metapath Evaluation + +```java +import dev.metaschema.oscal.lib.OscalBindingContext; +import dev.metaschema.oscal.lib.model.Catalog; +import dev.metaschema.core.metapath.MetapathExpression; +import dev.metaschema.core.metapath.IMetapathExpression; +import dev.metaschema.core.metapath.item.IItem; +import dev.metaschema.core.metapath.item.ISequence; +import dev.metaschema.databind.io.Format; +import dev.metaschema.databind.io.IDeserializer; +import java.nio.file.Path; + +OscalBindingContext context = OscalBindingContext.instance(); + +// Load a catalog +IDeserializer deserializer = context.newDeserializer( + Format.JSON, Catalog.class); +Catalog catalog = deserializer.deserialize(Path.of("catalog.json")); + +// Compile an expression +IMetapathExpression expression = MetapathExpression.compile( + "//control", context.getStaticContext()); + +// Evaluate against the catalog +ISequence results = expression.evaluate(catalog); + +// Process results +for (IItem item : results) { + System.out.println("Found: " + item.toAtomicItem().asString()); +} +``` + +## Common Query Patterns + +### Find All Controls + +```java +IMetapathExpression expr = MetapathExpression.compile( + "//control", context.getStaticContext()); +ISequence controls = expr.evaluate(catalog); +``` + +### Find Control by ID + +```java +IMetapathExpression expr = MetapathExpression.compile( + "//control[@id='ac-1']", context.getStaticContext()); +ISequence result = expr.evaluate(catalog); +``` + +### Get Control Titles + +```java +IMetapathExpression expr = MetapathExpression.compile( + "//control/title", context.getStaticContext()); +ISequence titles = expr.evaluate(catalog); + +titles.forEach(title -> + System.out.println(title.toAtomicItem().asString())); +``` + +### Count Controls + +```java +IMetapathExpression expr = MetapathExpression.compile( + "count(//control)", context.getStaticContext()); +ISequence result = expr.evaluate(catalog); + +int count = result.getFirstItem(true) + .toAtomicItem() + .asInteger() + .intValue(); +System.out.println("Control count: " + count); +``` + +### Find Controls with Specific Properties + +```java +// Find controls with status=withdrawn +IMetapathExpression expr = MetapathExpression.compile( + "//control[prop[@name='status'][@value='withdrawn']]", + context.getStaticContext()); +``` + +## Working with Results + +### Getting Bound Objects + +```java +import dev.metaschema.databind.model.IBoundObject; +import dev.metaschema.oscal.lib.model.Control; + +ISequence results = expression.evaluate(catalog); + +for (IItem item : results) { + if (item instanceof IBoundObject) { + Object value = ((IBoundObject) item).getValue(); + if (value instanceof Control) { + Control control = (Control) value; + System.out.println("Control ID: " + control.getId()); + System.out.println("Title: " + control.getTitle()); + } + } +} +``` + +### Getting Atomic Values + +```java +IMetapathExpression expr = MetapathExpression.compile( + "//control/@id", context.getStaticContext()); +ISequence ids = expr.evaluate(catalog); + +ids.forEach(item -> { + String id = item.toAtomicItem().asString(); + System.out.println("ID: " + id); +}); +``` + +## OSCAL-Specific Functions + +liboscal-java provides OSCAL-specific Metapath functions: + +### has-oscal-namespace + +Check if a property has an OSCAL namespace: + +```java +IMetapathExpression expr = MetapathExpression.compile( + "//prop[has-oscal-namespace(., 'https://fedramp.gov/ns/oscal')]", + context.getStaticContext()); +``` + +### resolve-profile + +Resolve a profile import (within Metapath): + +```java +IMetapathExpression expr = MetapathExpression.compile( + "resolve-profile(import)", + context.getStaticContext()); +``` + +## Query Examples by Document Type + +### Catalog Queries + +```java +// All control groups +"//group" + +// Controls in a specific group +"//group[@id='access-control']/control" + +// Control enhancements +"//control[contains(@id, '.')]" + +// All parameters +"//param" +``` + +### Profile Queries + +```java +// All imports +"//import" + +// Included controls +"//import/include-controls/with-id" + +// Parameter modifications +"//modify/set-parameter" +``` + +### SSP Queries + +```java +// System name +"/system-security-plan/system-characteristics/system-name" + +// Implemented requirements +"//implemented-requirement" + +// Components +"//component" +``` + +## Error Handling + +```java +import dev.metaschema.core.metapath.MetapathException; + +try { + IMetapathExpression expr = MetapathExpression.compile( + "//control[invalid syntax", context.getStaticContext()); +} catch (MetapathException e) { + System.err.println("Invalid expression: " + e.getMessage()); +} +``` + +## Caching Compiled Expressions + +For repeated queries, cache compiled expressions: + +```java +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class MetapathQueryCache { + private final Map cache = + new ConcurrentHashMap<>(); + private final StaticContext staticContext; + + public MetapathQueryCache(OscalBindingContext context) { + this.staticContext = context.getStaticContext(); + } + + public IMetapathExpression getExpression(String path) { + return cache.computeIfAbsent(path, p -> + MetapathExpression.compile(p, staticContext)); + } + + public ISequence query(Object document, String path) { + return getExpression(path).evaluate(document); + } +} +``` + +## Path Syntax Reference + +| Expression | Description | +|------------|-------------| +| `/catalog` | Root catalog | +| `//control` | All controls at any depth | +| `control[@id='ac-1']` | Control with specific ID | +| `control[1]` | First control | +| `control[last()]` | Last control | +| `control/title` | Titles of direct child controls | +| `..` | Parent element | +| `.` | Current element | + +## Function Reference + +| Function | Description | +|----------|-------------| +| `count(seq)` | Count items | +| `string-length(str)` | String length | +| `starts-with(str, prefix)` | Check prefix | +| `contains(str, substr)` | Check substring | +| `not(expr)` | Logical negation | +| `exists(seq)` | Check if non-empty | + +## Best Practices + +1. **Compile once, use many** - Cache compiled expressions +2. **Be specific** - Narrow paths are faster than `//` +3. **Handle empty results** - Check sequence length before accessing +4. **Use predicates** - Filter in the expression, not in Java +5. **Test expressions** - Validate with CLI before embedding + +## Next Steps + +Continue learning about liboscal-java with these related guides: + +- [Validating with Constraints](validating-with-constraints.html) - Validate queried data +- [Reading & Writing Data](reading-writing-data.html) - Load documents to query +- [Architecture](architecture.html) - Understand Metapath internals diff --git a/src/site/markdown/guides/loading-oscal-modules.md.vm b/src/site/markdown/guides/loading-oscal-modules.md.vm new file mode 100644 index 00000000..d20052bb --- /dev/null +++ b/src/site/markdown/guides/loading-oscal-modules.md.vm @@ -0,0 +1,228 @@ +# Using the OSCAL Binding Context + +This guide explains how to initialize and use the `OscalBindingContext`, which is the foundation for all OSCAL operations in liboscal-java. + +## What is the Binding Context? + +Before you can read, write, or validate OSCAL documents, you need a **binding context**. The binding context is the central object that: + +- Knows about all OSCAL document types (Catalog, Profile, SSP, etc.) +- Creates deserializers for reading OSCAL files +- Creates serializers for writing OSCAL files +- Provides access to Metapath evaluation +- Handles constraint validation + +Think of it as the "factory" that produces all the tools you need for OSCAL processing. In liboscal-java, the `OscalBindingContext` extends the base Metaschema binding context with OSCAL-specific configuration. + +## Overview + +The `OscalBindingContext` is the central entry point for working with OSCAL in liboscal-java. It provides: + +- Pre-configured access to all OSCAL model types +- Serialization and deserialization capabilities +- Metapath expression support +- Constraint validation + +## Getting the Binding Context + +### Standard Singleton + +For most use cases, use the singleton instance: + +```java +import dev.metaschema.oscal.lib.OscalBindingContext; + +OscalBindingContext context = OscalBindingContext.instance(); +``` + +### Why Use the Singleton? + +The `OscalBindingContext.instance()` method returns a pre-configured context that: + +- Loads all OSCAL Metaschema modules +- Registers OSCAL-specific Metapath functions +- Is thread-safe for concurrent use +- Avoids repeated module loading overhead + +## Working with OSCAL Types + +Once you have the context, you can work with any OSCAL document type: + +```java +import dev.metaschema.oscal.lib.model.Catalog; +import dev.metaschema.oscal.lib.model.Profile; +import dev.metaschema.oscal.lib.model.SystemSecurityPlan; + +// Create deserializers +IDeserializer catalogReader = context.newDeserializer( + Format.JSON, Catalog.class); +IDeserializer profileReader = context.newDeserializer( + Format.XML, Profile.class); +IDeserializer sspReader = context.newDeserializer( + Format.JSON, SystemSecurityPlan.class); +``` + +## Available OSCAL Document Types + +The binding context provides access to all OSCAL document types. Each type corresponds to a root element in the OSCAL specification: + +| Class | Description | +|:------|:------------| +| `Catalog` | OSCAL catalogs with controls and groups | +| `Profile` | Control selection and tailoring | +| `MappingCollection` | Control mapping between frameworks | +| `SystemSecurityPlan` | System security documentation | +| `ComponentDefinition` | Reusable component capabilities | +| `AssessmentPlan` | Security assessment planning | +| `AssessmentResults` | Assessment findings | +| `PlanOfActionAndMilestones` | POA&M tracking | + +## Model Mapping + +Understanding how OSCAL elements map to Java classes helps you navigate the API. The generated classes mirror the OSCAL model structure: + +### Document Structure + +Each OSCAL document type has a corresponding Java class with nested classes for its components: + +``` +Catalog (root) +├── Metadata # Document metadata (title, dates, parties) +│ ├── Property # Key-value properties +│ └── Link # Related resources +├── Group # Control groupings +│ └── Control # Individual controls +│ ├── Parameter # Control parameters +│ ├── Part # Prose sections +│ └── Property # Control properties +└── BackMatter # Referenced resources + └── Resource # External documents +``` + +### Common Patterns + +OSCAL uses consistent patterns across document types. Once you understand these patterns, working with any OSCAL type becomes intuitive: + +| OSCAL Concept | Java Pattern | Example | +|:--------------|:-------------|:--------| +| Root element | Top-level class | `Catalog`, `Profile` | +| Nested elements | Nested classes | `Catalog.Group`, `Control.Part` | +| Required UUID | `getUuid()` method | `catalog.getUuid()` | +| Metadata | `getMetadata()` method | `catalog.getMetadata()` | +| Back matter | `getBackMatter()` method | `catalog.getBackMatter()` | +| Properties | `getProps()` method | Returns `List` | +| Links | `getLinks()` method | Returns `List` | + +### Navigating the Model + +Use getter methods to traverse the document structure: + +```java +Catalog catalog = deserializer.deserialize(path); + +// Access metadata +Metadata metadata = catalog.getMetadata(); +String title = metadata.getTitle().toString(); + +// Iterate through groups and controls +for (Group group : catalog.getGroups()) { + System.out.println("Group: " + group.getTitle()); + + for (Control control : group.getControls()) { + System.out.println(" Control: " + control.getId() + " - " + control.getTitle()); + } +} +``` + +### Building Documents + +Create OSCAL documents programmatically using constructors and setters: + +```java +Catalog catalog = new Catalog(); +catalog.setUuid(UUID.randomUUID()); + +Metadata metadata = new Metadata(); +metadata.setTitle(MarkupLine.fromMarkdown("My Catalog")); +metadata.setLastModified(ZonedDateTime.now()); +metadata.setVersion("1.0.0"); +catalog.setMetadata(metadata); +``` + +## Understanding the Context Hierarchy + +``` +OscalBindingContext + └── Extends BindingContext (from metaschema-databind) + └── Loads OSCAL Metaschema modules + ├── oscal_catalog + ├── oscal_profile + ├── oscal_ssp + ├── oscal_component-definition + ├── oscal_assessment-plan + ├── oscal_assessment-results + └── oscal_poam +``` + +## Custom Configuration + +For advanced use cases, you can access the underlying Metaschema: + +```java +import dev.metaschema.core.model.IModule; + +// Get the OSCAL module +IModule oscalModule = context.getModuleByUri( + URI.create("http://csrc.nist.gov/ns/oscal/1.0")); +``` + +## Thread Safety + +The `OscalBindingContext.instance()` is: + +- **Thread-safe** for reading operations +- **Immutable** once created +- **Suitable** for use in multi-threaded applications + +```java +// Safe to use from multiple threads +ExecutorService executor = Executors.newFixedThreadPool(4); +OscalBindingContext context = OscalBindingContext.instance(); + +for (Path file : files) { + executor.submit(() -> { + // Each thread can safely use the same context + Catalog catalog = loadCatalog(context, file); + processCatalog(catalog); + }); +} +``` + +## Error Handling + +Handle module loading errors appropriately: + +```java +try { + OscalBindingContext context = OscalBindingContext.instance(); +} catch (MetaschemaException e) { + // Handle module loading failure + logger.error("Failed to load OSCAL modules", e); + throw new RuntimeException("OSCAL initialization failed", e); +} +``` + +## Best Practices + +1. **Use the singleton** - Avoid creating multiple context instances +2. **Reuse the context** - Pass the same context throughout your application +3. **Handle errors** - Catch and handle `MetaschemaException` appropriately +4. **Don't cache deserializers** - Create new deserializers as needed; they're lightweight + +## Next Steps + +Continue learning about liboscal-java with these related guides: + +- [Reading & Writing Data](reading-writing-data.html) - Load and save OSCAL documents +- [Resolving Profiles](resolving-profiles.html) - Resolve profiles programmatically +- [Architecture](architecture.html) - Understand the library structure diff --git a/src/site/markdown/guides/reading-writing-data.md.vm b/src/site/markdown/guides/reading-writing-data.md.vm new file mode 100644 index 00000000..30c05713 --- /dev/null +++ b/src/site/markdown/guides/reading-writing-data.md.vm @@ -0,0 +1,252 @@ +# Reading & Writing Data + +This guide explains how to read, write, and convert OSCAL documents using liboscal-java. + +## Overview + +One of the core capabilities of liboscal-java is format-agnostic serialization—the ability to read and write OSCAL documents in XML, JSON, and YAML formats using the same Java objects. This means you can: + +- Load OSCAL documents in any format and work with them as strongly-typed Java objects +- Convert between formats by reading in one format and writing in another +- Process documents without needing to know or care about the original format +- Output documents in whichever format your downstream tools require + +The library handles all format-specific details automatically. Whether your input is XML, JSON, or YAML, you get the same `Catalog`, `Profile`, or `SystemSecurityPlan` object. + +## Key Concepts + +Before diving into code, understand these core concepts: + +- **`OscalBindingContext`** - The entry point for all serialization operations. Use the singleton instance via `OscalBindingContext.instance()`. +- **`IDeserializer`** - Reads a specific document type (e.g., `Catalog`) from a file, URL, or stream +- **`ISerializer`** - Writes a specific document type to a file or stream +- **`Format`** - Enum specifying the data format: `Format.XML`, `Format.JSON`, or `Format.YAML` + +## Reading OSCAL Documents + +### From a File + +```java +import dev.metaschema.oscal.lib.OscalBindingContext; +import dev.metaschema.oscal.lib.model.Catalog; +import dev.metaschema.databind.io.Format; +import dev.metaschema.databind.io.IDeserializer; + +import java.nio.file.Path; + +OscalBindingContext context = OscalBindingContext.instance(); + +// Read JSON +IDeserializer deserializer = context.newDeserializer( + Format.JSON, Catalog.class); +Catalog catalog = deserializer.deserialize(Path.of("catalog.json")); + +// Read XML +deserializer = context.newDeserializer(Format.XML, Catalog.class); +catalog = deserializer.deserialize(Path.of("catalog.xml")); + +// Read YAML +deserializer = context.newDeserializer(Format.YAML, Catalog.class); +catalog = deserializer.deserialize(Path.of("catalog.yaml")); +``` + +### From a URL + +```java +import java.net.URI; + +IDeserializer deserializer = context.newDeserializer( + Format.JSON, Catalog.class); +Catalog catalog = deserializer.deserialize( + URI.create("https://example.com/catalog.json").toURL()); +``` + +### From an InputStream + +```java +import java.io.InputStream; + +try (InputStream is = getClass().getResourceAsStream("/catalog.json")) { + IDeserializer deserializer = context.newDeserializer( + Format.JSON, Catalog.class); + Catalog catalog = deserializer.deserialize(is, + URI.create("classpath:/catalog.json")); +} +``` + +### Auto-detecting Format + +Detect format from file extension: + +```java +Path file = Path.of("document.json"); +Format format = Format.valueOf(file); // Returns Format.JSON + +IDeserializer deserializer = context.newDeserializer( + format, Catalog.class); +``` + +## Writing OSCAL Documents + +### To a File + +```java +import dev.metaschema.databind.io.ISerializer; + +ISerializer serializer = context.newSerializer( + Format.JSON, Catalog.class); +serializer.serialize(catalog, Path.of("output.json")); +``` + +### To an OutputStream + +```java +import java.io.OutputStream; +import java.nio.file.Files; + +try (OutputStream os = Files.newOutputStream(Path.of("output.json"))) { + ISerializer serializer = context.newSerializer( + Format.JSON, Catalog.class); + serializer.serialize(catalog, os); +} +``` + +### To a String + +```java +import java.io.StringWriter; + +StringWriter writer = new StringWriter(); +ISerializer serializer = context.newSerializer( + Format.JSON, Catalog.class); +serializer.serialize(catalog, writer); +String json = writer.toString(); +``` + +## Converting Between Formats + +### XML to JSON + +```java +// Read XML +IDeserializer xmlReader = context.newDeserializer( + Format.XML, Catalog.class); +Catalog catalog = xmlReader.deserialize(Path.of("catalog.xml")); + +// Write JSON +ISerializer jsonWriter = context.newSerializer( + Format.JSON, Catalog.class); +jsonWriter.serialize(catalog, Path.of("catalog.json")); +``` + +### Batch Conversion + +```java +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Stream; + +public void convertDirectory(Path inputDir, Path outputDir, + Format inputFormat, Format outputFormat) throws IOException { + + OscalBindingContext context = OscalBindingContext.instance(); + + try (Stream files = Files.list(inputDir)) { + files.filter(p -> Format.valueOf(p) == inputFormat) + .forEach(inputPath -> { + try { + // Read + IDeserializer reader = context.newDeserializer( + inputFormat, Catalog.class); + Catalog catalog = reader.deserialize(inputPath); + + // Write + String outputName = inputPath.getFileName().toString() + .replaceAll("\\.[^.]+$", outputFormat.getDefaultExtension()); + Path outputPath = outputDir.resolve(outputName); + + ISerializer writer = context.newSerializer( + outputFormat, Catalog.class); + writer.serialize(catalog, outputPath); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + } +} +``` + +## Working with Different Document Types + +### System Security Plans + +```java +import dev.metaschema.oscal.lib.model.SystemSecurityPlan; + +IDeserializer deserializer = context.newDeserializer( + Format.JSON, SystemSecurityPlan.class); +SystemSecurityPlan ssp = deserializer.deserialize(Path.of("ssp.json")); + +// Access SSP content +System.out.println("System Name: " + + ssp.getSystemCharacteristics().getSystemName()); +``` + +### Profiles + +```java +import dev.metaschema.oscal.lib.model.Profile; + +IDeserializer deserializer = context.newDeserializer( + Format.JSON, Profile.class); +Profile profile = deserializer.deserialize(Path.of("profile.json")); + +// Access imports +profile.getImports().forEach(imp -> + System.out.println("Imports: " + imp.getHref())); +``` + +### Component Definitions + +```java +import dev.metaschema.oscal.lib.model.ComponentDefinition; + +IDeserializer deserializer = context.newDeserializer( + Format.JSON, ComponentDefinition.class); +ComponentDefinition compDef = deserializer.deserialize( + Path.of("component.json")); +``` + +## Error Handling + +```java +import dev.metaschema.databind.io.DeserializationException; + +try { + Catalog catalog = deserializer.deserialize(Path.of("catalog.json")); +} catch (DeserializationException e) { + // Handle parsing errors + System.err.println("Failed to parse: " + e.getMessage()); +} catch (IOException e) { + // Handle I/O errors + System.err.println("Failed to read file: " + e.getMessage()); +} +``` + +## Best Practices + +1. **Use try-with-resources** for streams +2. **Detect format from extension** using `Format.valueOf(Path)` +3. **Reuse the binding context** - don't create new instances +4. **Handle exceptions** appropriately for your use case +5. **Close resources** properly when using streams + +## Next Steps + +Continue learning about liboscal-java with these related guides: + +- [Resolving Profiles](resolving-profiles.html) - Resolve profiles to catalogs +- [Executing Metapath](executing-metapath.html) - Query OSCAL data +- [Validating with Constraints](validating-with-constraints.html) - Validate content diff --git a/src/site/markdown/guides/resolving-profiles.md.vm b/src/site/markdown/guides/resolving-profiles.md.vm new file mode 100644 index 00000000..797f96bf --- /dev/null +++ b/src/site/markdown/guides/resolving-profiles.md.vm @@ -0,0 +1,252 @@ +# Resolving Profiles + +This guide explains how to resolve OSCAL profiles programmatically using liboscal-java. + +## What is Profile Resolution? + +OSCAL profiles are a powerful mechanism for customizing security control catalogs. Instead of copying and manually editing a catalog like NIST SP 800-53, organizations create profiles that: + +- **Select controls** - Choose which controls apply to their environment +- **Set parameters** - Fill in organization-specific values for control parameters +- **Modify content** - Add guidance, alter statements, or customize controls +- **Layer customizations** - Import other profiles to build on existing baselines + +However, many tools that consume OSCAL content expect a simple catalog, not a profile with references to external catalogs. **Profile resolution** is the process of "flattening" a profile into a self-contained catalog that includes all selected controls with all modifications applied. + +For example, if you have a profile that imports NIST SP 800-53 and selects the FedRAMP High baseline controls, profile resolution produces a catalog containing just those controls, with any organization-specific modifications already applied. + +## Overview + +Profile resolution takes an OSCAL profile and produces a **resolved catalog** containing: + +- Only the selected controls (not the entire source catalog) +- All parameter values set to their resolved values +- All modifications (additions, alterations) applied +- A single, self-contained document with no external dependencies + +## Basic Profile Resolution + +The `ProfileResolver` accepts file paths, URLs, or document nodes and returns an `IDocumentNodeItem` containing the resolved catalog: + +```java +import dev.metaschema.oscal.lib.OscalBindingContext; +import dev.metaschema.oscal.lib.model.Catalog; +import dev.metaschema.oscal.lib.profile.resolver.ProfileResolver; +import dev.metaschema.core.model.IDocumentNodeItem; + +import java.nio.file.Path; + +// Resolve a profile directly from a file path +ProfileResolver resolver = new ProfileResolver(); +IDocumentNodeItem resolvedDocument = resolver.resolve(Path.of("profile.json")); + +// Extract the catalog from the resolved document +Catalog resolvedCatalog = (Catalog) resolvedDocument.getValue(); +``` + +## Resolution with Custom Document Loader + +For profiles that import external resources, configure a document loader: + +```java +import dev.metaschema.databind.io.DefaultBoundLoader; + +// Get the binding context +OscalBindingContext context = OscalBindingContext.instance(); + +// Create a document loader with the context +DefaultBoundLoader loader = new DefaultBoundLoader(context); + +// Configure resolver with custom loader +ProfileResolver resolver = new ProfileResolver(); +resolver.setDocumentLoader(loader); + +// Resolve the profile +IDocumentNodeItem resolvedDocument = resolver.resolve(Path.of("profile.json")); +Catalog resolvedCatalog = (Catalog) resolvedDocument.getValue(); +``` + +## Saving the Resolved Catalog + +```java +import dev.metaschema.databind.io.ISerializer; + +// Write the resolved catalog +ISerializer serializer = context.newSerializer( + Format.JSON, Catalog.class); +serializer.serialize(resolvedCatalog, Path.of("resolved-catalog.json")); +``` + +## Complete Example + +```java +import dev.metaschema.oscal.lib.OscalBindingContext; +import dev.metaschema.oscal.lib.model.Catalog; +import dev.metaschema.oscal.lib.model.Profile; +import dev.metaschema.oscal.lib.profile.resolver.ProfileResolver; +import dev.metaschema.databind.io.Format; +import dev.metaschema.databind.io.IDeserializer; +import dev.metaschema.databind.io.ISerializer; + +import java.nio.file.Path; + +public class ProfileResolutionExample { + + public static void main(String[] args) throws Exception { + // Get binding context + OscalBindingContext context = OscalBindingContext.instance(); + + // Load profile + IDeserializer profileReader = context.newDeserializer( + Format.JSON, Profile.class); + Profile profile = profileReader.deserialize( + Path.of("fedramp-high-profile.json")); + + // Resolve + ProfileResolver resolver = new ProfileResolver(); + Catalog resolved = resolver.resolve(profile); + + // Save result + ISerializer catalogWriter = context.newSerializer( + Format.JSON, Catalog.class); + catalogWriter.serialize(resolved, + Path.of("fedramp-high-resolved.json")); + + System.out.println("Resolved " + + resolved.getGroups().stream() + .flatMap(g -> g.getControls().stream()) + .count() + " controls"); + } +} +``` + +## Profile Resolution Pipeline + +Understanding what happens during resolution: + +``` +Profile + │ + ├── 1. Load imported catalogs/profiles (recursive) + │ + ├── 2. Select controls (include/exclude) + │ + ├── 3. Apply modifications + │ ├── Set parameters + │ ├── Add content + │ └── Alter existing content + │ + ├── 4. Merge controls (from multiple imports) + │ + └── 5. Generate resolved catalog +``` + +## Handling Remote Resources + +When profiles import remote catalogs: + +```java +import java.net.URI; + +// Profile imports: "href": "https://example.com/catalog.json" + +// The resolver will fetch remote resources automatically +ProfileResolver resolver = new ProfileResolver(); +Catalog resolved = resolver.resolve(profile); +``` + +## Working with Profile Chains + +Profiles can import other profiles (chaining): + +``` +Base Catalog + ↓ +Profile A (selects controls) + ↓ +Profile B (adds customizations) + ↓ +Profile C (organization-specific) + ↓ +Resolved Catalog +``` + +Resolution handles chains automatically: + +```java +// Even if profile imports another profile, resolution is handled +ProfileResolver resolver = new ProfileResolver(); +Catalog resolved = resolver.resolve(organizationProfile); +``` + +## Error Handling + +```java +import dev.metaschema.oscal.lib.profile.resolver.ProfileResolutionException; + +try { + Catalog resolved = resolver.resolve(profile); +} catch (ProfileResolutionException e) { + System.err.println("Resolution failed: " + e.getMessage()); + // Handle specific resolution errors +} catch (IOException e) { + System.err.println("Failed to load import: " + e.getMessage()); + // Handle I/O errors +} +``` + +## Accessing Resolved Content + +After resolution, work with the catalog: + +```java +Catalog resolved = resolver.resolve(profile); + +// Iterate controls +resolved.getGroups().forEach(group -> { + System.out.println("Group: " + group.getTitle()); + + group.getControls().forEach(control -> { + System.out.println(" Control: " + control.getId() + + " - " + control.getTitle()); + }); +}); + +// Find specific control +resolved.getGroups().stream() + .flatMap(g -> g.getControls().stream()) + .filter(c -> c.getId().equals("ac-1")) + .findFirst() + .ifPresent(control -> { + System.out.println("Found: " + control.getTitle()); + }); +``` + +## Resolution Options + +The resolver supports various options through the profile structure: + +| Profile Element | Effect on Resolution | +|-----------------|----------------------| +| `import/include-all` | Include all controls from source | +| `import/include-controls` | Include specific controls | +| `import/exclude-controls` | Exclude specific controls | +| `merge/combine` | How to combine duplicate controls | +| `merge/flat` | Flatten control hierarchy | +| `modify/set-parameters` | Set parameter values | +| `modify/alters` | Modify control content | + +## Best Practices + +1. **Cache resolved catalogs** - Resolution can be expensive +2. **Handle remote failures** - Network requests may fail +3. **Validate after resolution** - Ensure result is valid +4. **Check for circular imports** - Can cause infinite loops + +## Next Steps + +Continue learning about liboscal-java with these related guides: + +- [Reading & Writing Data](reading-writing-data.html) - Save resolved catalogs +- [Executing Metapath](executing-metapath.html) - Query resolved content +- [Validating with Constraints](validating-with-constraints.html) - Validate results diff --git a/src/site/markdown/guides/validating-with-constraints.md.vm b/src/site/markdown/guides/validating-with-constraints.md.vm new file mode 100644 index 00000000..eb5f84b4 --- /dev/null +++ b/src/site/markdown/guides/validating-with-constraints.md.vm @@ -0,0 +1,255 @@ +# Validating with Constraints + +This guide explains how to validate OSCAL documents using Metaschema constraints. + +## Overview + +Metaschema constraints provide validation beyond schema compliance: + +- **Cardinality** - Required fields, maximum occurrences +- **Allowed values** - Enumerated value restrictions +- **Patterns** - Regex-based validation +- **Uniqueness** - Key constraints within scopes +- **Cross-references** - Reference integrity +- **Custom rules** - Metapath-based assertions + +## Basic Validation + +### Validate During Deserialization + +```java +import dev.metaschema.databind.io.DeserializationFeature; +import dev.metaschema.databind.io.IBoundLoader; +import dev.metaschema.oscal.lib.OscalBindingContext; +import dev.metaschema.oscal.lib.model.Catalog; + +import java.nio.file.Path; + +OscalBindingContext context = OscalBindingContext.instance(); +IBoundLoader loader = context.newBoundLoader(); + +// Enable constraint validation during loading +loader.enableFeature(DeserializationFeature.DESERIALIZE_VALIDATE_CONSTRAINTS); + +Catalog catalog = loader.load(Catalog.class, Path.of("catalog.json")); +``` + +### Validate Existing Objects + +```java +import dev.metaschema.core.model.validation.IValidationResult; +import dev.metaschema.oscal.lib.OscalBindingContext; + +import java.net.URI; +import java.nio.file.Path; + +OscalBindingContext context = OscalBindingContext.instance(); +URI target = Path.of("catalog.json").toUri(); + +// Validate against constraints +IValidationResult result = context.validateWithConstraints(target, null); + +if (!result.isPassing()) { + result.getFindings().forEach(finding -> { + System.err.println(finding.getSeverity() + ": " + + finding.getMessage()); + }); +} +``` + +## Working with Validation Results + +```java +import dev.metaschema.core.model.constraint.IConstraint.Level; +import dev.metaschema.core.model.validation.IValidationFinding; +import dev.metaschema.core.model.validation.IValidationResult; +import dev.metaschema.oscal.lib.OscalBindingContext; + +import java.net.URI; +import java.nio.file.Path; + +OscalBindingContext context = OscalBindingContext.instance(); +URI target = Path.of("catalog.json").toUri(); + +IValidationResult result = context.validateWithConstraints(target, null); + +// Check if validation passed +if (result.isPassing()) { + System.out.println("Validation passed"); +} else { + System.out.println("Validation failed"); +} + +// Process findings +for (IValidationFinding finding : result.getFindings()) { + System.out.println(String.format("[%s] %s at %s", + finding.getSeverity(), + finding.getMessage(), + finding.getLocation())); +} +``` + +## Understanding Finding Severity + +| Severity | Meaning | +|----------|---------| +| `CRITICAL` | Severe error, document unusable | +| `ERROR` | Constraint violation | +| `WARNING` | Potential issue, may be intentional | +| `INFORMATIONAL` | Note for awareness | + +## Using FindingCollectingConstraintValidationHandler + +The framework provides `FindingCollectingConstraintValidationHandler` for collecting validation findings: + +```java +import dev.metaschema.core.model.constraint.FindingCollectingConstraintValidationHandler; +import dev.metaschema.core.model.constraint.IConstraint.Level; + +// The handler implements IValidationResult +FindingCollectingConstraintValidationHandler handler = + new FindingCollectingConstraintValidationHandler(); + +// After validation completes, check results +if (!handler.isPassing()) { + handler.getFindings().forEach(finding -> { + System.err.println(finding.getSeverity() + ": " + + finding.getMessage()); + }); +} + +// Check highest severity level +Level highestSeverity = handler.getHighestSeverity(); +if (highestSeverity.ordinal() >= Level.ERROR.ordinal()) { + System.err.println("Validation failed with errors"); +} +``` + +## Common OSCAL Validation Errors + +### Missing Required Fields + +``` +ERROR: Required field 'title' is missing at /catalog/metadata +``` + +**Fix:** Add the required metadata title: + +```json +{ + "catalog": { + "metadata": { + "title": "My Catalog" + } + } +} +``` + +### Invalid UUID + +``` +ERROR: Value 'not-a-uuid' is not a valid UUID at /catalog/uuid +``` + +**Fix:** Use a valid UUID v4 format. + +### Invalid Reference + +``` +ERROR: Reference 'control-999' not found at /profile/modify/set-parameters/0/param-id +``` + +**Fix:** Ensure referenced elements exist. + +### Constraint Violation + +``` +ERROR: Value 'unknown' is not allowed for property 'status' at /catalog/controls/0/props/0 +``` + +**Fix:** Use allowed values defined by constraints. + +## Validating Specific Document Types + +### System Security Plans + +```java +import dev.metaschema.oscal.lib.model.SystemSecurityPlan; + +IDeserializer deserializer = context.newDeserializer( + Format.JSON, SystemSecurityPlan.class); +deserializer.setConstraintValidationEnabled(true); + +SystemSecurityPlan ssp = deserializer.deserialize(Path.of("ssp.json")); +``` + +### Profiles + +```java +import dev.metaschema.oscal.lib.model.Profile; + +// Note: Profile validation includes checking import references +IDeserializer deserializer = context.newDeserializer( + Format.JSON, Profile.class); +deserializer.setConstraintValidationEnabled(true); + +Profile profile = deserializer.deserialize(Path.of("profile.json")); +``` + +## Validation in CI/CD + +```java +public class OscalValidator { + + public static void main(String[] args) { + if (args.length < 1) { + System.err.println("Usage: java OscalValidator "); + System.exit(2); + } + + Path file = Path.of(args[0]); + OscalBindingContext context = OscalBindingContext.instance(); + + try { + IDeserializer deserializer = context.newDeserializer( + Format.valueOf(file), Catalog.class); + deserializer.setConstraintValidationEnabled(true); + + Catalog catalog = deserializer.deserialize(file); + IValidationResult result = context.validate(catalog); + + if (result.isPassing()) { + System.out.println("Validation passed"); + System.exit(0); + } else { + result.getFindings().forEach(f -> + System.err.println(f.getSeverity() + ": " + f.getMessage())); + System.exit(1); + } + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + System.exit(2); + } + } +} +``` + +## Best Practices + +1. **Validate early** - Check documents as soon as they're loaded +2. **Log all findings** - Even warnings may indicate issues +3. **Fail fast on errors** - Don't process invalid documents +4. **Report locations** - Include XPath/JSON Pointer for debugging +5. **Consider severity** - Some warnings may be acceptable + +## Constraint Validation Status + +> **Note:** Metaschema constraint validation is experimental. Some constraints may not be fully enforced. Schema-level validation (well-formedness, structure) is stable. + +## Next Steps + +Continue learning about liboscal-java with these related guides: + +- [Reading & Writing Data](reading-writing-data.html) - Load documents for validation +- [Executing Metapath](executing-metapath.html) - Query validated content +- [Architecture](architecture.html) - Understand the validation pipeline diff --git a/src/site/markdown/index.md.vm b/src/site/markdown/index.md.vm index 557986f7..921faa29 100644 --- a/src/site/markdown/index.md.vm +++ b/src/site/markdown/index.md.vm @@ -1,26 +1,135 @@ # ${project.name} -A Java library to support processing OSCAL content. +A Java library for processing [OSCAL](https://pages.nist.gov/OSCAL/) (Open Security Controls Assessment Language) content. This library enables Java developers to create, read, validate, and transform OSCAL documents. -This open-source, Metaschema Java library offers a programmatic means to work with [OSCAL](https://pages.nist.gov/OSCAL/) models defined by the [Metaschema modeling language](https://github.com/metaschema-framework/metaschema). This framework also supports programmatically creating, modifying, parsing, and writing XML, JSON, and YAML OSCAL instance data. This work is intended to make it easier for Java software developers to incorporate OSCAL-based capabilities into their applications. +## What is OSCAL? -The following features are supported by this library: -- Reading and writing OSCAL documents in XML, JSON, and YAML formats into a common Java object model. -- Resolution of OSCAL profiles to [produce resolved catalogs](https://pages.nist.gov/OSCAL/concepts/processing/profile-resolution/). -- Validation of OSCAL content [well-formedness and validation](https://pages.nist.gov/OSCAL/concepts/validation/) of OSCAL syntax using XML and JSON schemas. -- (Experimental) Validation of OSCAL content using [Metaschema](https://metaschema.dev/) constraints to enforce allowed values, cross-references, and some conditionally required data elements. -- Builders for programmatically creating common OSCAL data elements. +OSCAL is a set of standardized formats developed by [NIST](https://www.nist.gov/) for expressing security controls, control baselines, system security plans, assessment plans and results, and plans of action and milestones. Organizations use OSCAL to: -This library is based on the [Metaschema Java Tools](https://metaschema-java.metaschema.dev/) project. +- **Document security controls** - Express control catalogs (like NIST SP 800-53) in a machine-readable format +- **Define control baselines** - Create profiles that select and customize controls for specific use cases +- **Create system security plans** - Document how systems implement required controls +- **Automate security assessments** - Define assessment procedures and capture results in structured formats +- **Track remediation** - Manage plans of action and milestones (POA&Ms) for security findings -# Usage +OSCAL supports XML, JSON, and YAML representations of all document types, with equivalent semantics across all formats. This means you can choose the format that best fits your toolchain while maintaining full compatibility with other OSCAL-compliant tools. -The following dependency can be added to your POM to use this library. +## Why Use This Library? + +This library provides a complete Java solution for working with OSCAL content: + +- **Type-safe access** - Work with strongly-typed Java objects rather than parsing XML or JSON directly. The compiler catches errors that would otherwise appear at runtime. + +- **Format flexibility** - Read OSCAL from any supported format (XML, JSON, YAML) and write to any format. Convert between formats without losing data. + +- **Profile resolution** - Resolve OSCAL profiles to produce flattened catalogs that include all selected controls with modifications applied. + +- **Validation** - Validate OSCAL documents against the official schemas and constraint rules to ensure well-formed, valid content. + +- **Built on Metaschema** - Leverages the [Metaschema Java](https://metaschema-java.metaschema.dev/) framework, ensuring the Java model stays synchronized with the official OSCAL specification. + +## Quick Start + +Add the dependency to your Maven project: ```xml - ${project.groupId} - ${project.artifactId} - ${project.version} + ${project.groupId} + ${project.artifactId} + ${project.version} ``` + +Load and work with an OSCAL catalog: + +```java +import dev.metaschema.oscal.lib.OscalBindingContext; +import dev.metaschema.oscal.lib.model.Catalog; +import java.nio.file.Path; + +// Get the binding context +OscalBindingContext context = OscalBindingContext.instance(); + +// Load a catalog using the convenience method +Catalog catalog = context.loadCatalog(Path.of("catalog.json")); + +// Access content +System.out.println("Catalog: " + catalog.getMetadata().getTitle()); +``` + +See the [Installation](installation.html) guide for complete setup instructions. + +## Key Features + +### Multi-format Support + +Read and write OSCAL documents in XML, JSON, and YAML formats. The library automatically detects input format and handles all serialization details. Convert between formats with a simple API call—no manual transformation required. + +### Profile Resolution + +OSCAL profiles allow organizations to customize control catalogs by selecting controls, modifying parameter values, and adding organization-specific guidance. Profile resolution produces a "resolved catalog" that flattens all selections and modifications into a single catalog document. This library implements the complete profile resolution algorithm as specified in the OSCAL specification. + +### Validation + +Validate OSCAL documents at multiple levels: +- **Well-formedness** - Ensure documents are valid XML, JSON, or YAML +- **Schema compliance** - Verify documents conform to the OSCAL schema structure +- **Constraint validation** - (Experimental) Check documents against Metaschema constraint rules that express business logic beyond schema requirements + +### Programmatic Construction + +Build OSCAL documents programmatically using Java builder patterns. Create catalogs, profiles, system security plans, and other OSCAL documents entirely in code, then serialize to any supported format. + +## Supported OSCAL Document Types + +This library supports all OSCAL model types. Each document type has a corresponding Java class in the `dev.metaschema.oscal.lib.model` package: + +| Document Type | Class | Purpose | +|:--------------|:------|:--------| +| Catalog | `Catalog` | Collections of security controls (e.g., NIST SP 800-53) | +| Profile | `Profile` | Control baselines that select and customize catalog controls | +| Mapping | `MappingCollection` | Relationships between control frameworks | +| System Security Plan | `SystemSecurityPlan` | Documentation of how a system implements controls | +| Component Definition | `ComponentDefinition` | Reusable security capabilities and components | +| Assessment Plan | `AssessmentPlan` | Plans for assessing control implementation | +| Assessment Results | `AssessmentResults` | Results from security assessments | +| POA&M | `PlanOfActionAndMilestones` | Plans for addressing security findings | + +## Getting Started + +| Task | Guide | +|:-----|:------| +| Add to your project | [Installation](installation.html) | +| Build from source | [Building](building.html) | +| Understand the binding context | [Using the Binding Context](guides/loading-oscal-modules.html) | +| Read and write data | [Reading & Writing Data](guides/reading-writing-data.html) | +| Resolve profiles | [Resolving Profiles](guides/resolving-profiles.html) | +| Query with Metapath | [Executing Metapath](guides/executing-metapath.html) | +| Validate content | [Validating with Constraints](guides/validating-with-constraints.html) | + +## Architecture + +This library is built on the [Metaschema Java Tools](https://metaschema-java.metaschema.dev/) project. OSCAL model classes are generated from the OSCAL Metaschema definitions during the build process. + +See the [Architecture](guides/architecture.html) guide for details on the library structure. + +## Related Projects + +This library is part of a larger ecosystem of OSCAL and Metaschema tools: + +| Project | Description | +|:--------|:------------| +| [oscal-cli](https://github.com/metaschema-framework/oscal-cli) | Command-line tool for OSCAL operations | +| [metaschema-java](https://github.com/metaschema-framework/metaschema-java) | Core Metaschema framework | +| [OSCAL](https://pages.nist.gov/OSCAL/) | OSCAL specification and documentation | + +## Using with Claude Code + +This project includes plugins for [Claude Code](https://claude.ai/code) that provide AI-assisted development. See the [Claude Integration](claude-integration.html) guide for details. + +## Support + +Have questions or found an issue? Here's how to get help: + +- [GitHub Issues](${project.issueManagement.url}) - Report bugs or request features +- [Contributing](${project.scm.url}/blob/develop/CONTRIBUTING.md) - Contribution guidelines diff --git a/src/site/markdown/installation.md.vm b/src/site/markdown/installation.md.vm new file mode 100644 index 00000000..178032fd --- /dev/null +++ b/src/site/markdown/installation.md.vm @@ -0,0 +1,174 @@ +# Installation + +This guide explains how to add ${project.name} to your Java project. + +## Overview + +This library provides everything you need to work with OSCAL documents in Java. Adding this single dependency gives you: + +- Pre-generated Java model classes for all OSCAL document types +- Serialization support for XML, JSON, and YAML formats +- Profile resolution capabilities +- Validation tools +- The underlying [Metaschema Java](https://metaschema-java.metaschema.dev/) framework + +## Prerequisites + +Before installing, ensure your development environment meets these requirements: + +| Requirement | Minimum Version | Recommended | +|:------------|:----------------|:------------| +| Java JDK | 11 | 17 or later | +| Maven | 3.9.0 | Latest | +| Gradle | 7.0 | Latest | + +The library targets Java 11 for broad compatibility, but building from source requires Java 17. See [Building from Source](building.html) if you need to compile the library yourself. + +## Maven + +### Release Versions + +Stable releases are published to Maven Central and require no additional repository configuration. Add the following dependency to your `pom.xml`: + +```xml + + ${project.groupId} + ${project.artifactId} + ${project.version} + +``` + +Release versions are available from [Maven Central](https://central.sonatype.com/artifact/dev.metaschema.oscal/liboscal-java). + +### Snapshot Versions + +Snapshot versions contain the latest changes from the `develop` branch, including new features, bug fixes, and support for newer OSCAL releases that haven't yet been officially released. These are useful for testing upcoming changes or accessing features before they're released, but they may be less stable than release versions. + +To use snapshots, add the project's snapshot repository: + +```xml + + + sonatype-snapshots + https://oss.sonatype.org/content/repositories/snapshots/ + + true + + + + + + + ${project.groupId} + ${project.artifactId} + ${project.version} + + +``` + +## Gradle + +Gradle users can add the library using either Kotlin DSL or Groovy DSL syntax. + +### Kotlin DSL + +Add to your `build.gradle.kts`: + +```kotlin +dependencies { + implementation("${project.groupId}:${project.artifactId}:${project.version}") +} +``` + +For snapshot versions, add the repository: + +```kotlin +repositories { + mavenCentral() + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots/") + } +} +``` + +### Groovy DSL + +Add to your `build.gradle`: + +```groovy +dependencies { + implementation '${project.groupId}:${project.artifactId}:${project.version}' +} +``` + +For snapshot versions, add the repository: + +```groovy +repositories { + mavenCentral() + maven { + url 'https://oss.sonatype.org/content/repositories/snapshots/' + } +} +``` + +## Building from Source + +If you need to build the library from source—for example, to contribute changes, debug issues, or access unreleased features—see the [Building](building.html) guide. Building from source requires Java 17 or later. + +## IDE Setup + +Modern Java IDEs automatically recognize Maven and Gradle projects, downloading dependencies and configuring the classpath. After adding the dependency to your build file: + +1. **IntelliJ IDEA**: Open the project folder or `pom.xml`/`build.gradle` file. IntelliJ will import the project and download dependencies automatically. + +2. **Eclipse**: Import as a Maven or Gradle project using the built-in import wizard. The [M2Eclipse](https://www.eclipse.org/m2e/) plugin handles Maven projects. + +3. **VS Code**: Install the [Extension Pack for Java](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-pack), then open the project folder. VS Code will detect the build system and configure accordingly. + +For more detailed IDE setup, refer to your IDE's documentation: + +- [IntelliJ IDEA - Maven Projects](https://www.jetbrains.com/help/idea/maven-support.html) +- [Eclipse - Maven Integration](https://www.eclipse.org/m2e/) +- [VS Code - Java Extension Pack](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-pack) + +## Verifying Installation + +After adding the dependency, verify the installation by loading a sample OSCAL document: + +```java +import dev.metaschema.oscal.lib.OscalBindingContext; +import dev.metaschema.oscal.lib.model.Catalog; +import dev.metaschema.databind.io.Format; +import dev.metaschema.databind.io.IDeserializer; +import java.net.URI; + +public class VerifyInstallation { + public static void main(String[] args) throws Exception { + OscalBindingContext context = OscalBindingContext.instance(); + + // Load the NIST SP 800-53 catalog from the official OSCAL content repository + URI catalogUri = URI.create( + "https://raw.githubusercontent.com/usnistgov/oscal-content/main/" + + "nist.gov/SP800-53/rev5/json/NIST_SP-800-53_rev5_catalog.json"); + + IDeserializer deserializer = context.newDeserializer( + Format.JSON, Catalog.class); + Catalog catalog = deserializer.deserialize(catalogUri); + + System.out.println("Successfully loaded: " + catalog.getMetadata().getTitle()); + System.out.println("Number of groups: " + catalog.getGroups().size()); + } +} +``` + +If this compiles and runs without errors, the library is correctly installed. + +## Next Steps + +Once installed, explore these guides to start using the library: + +- [Loading OSCAL Modules](guides/loading-oscal-modules.html) - Understand the OSCAL binding context +- [Reading & Writing Data](guides/reading-writing-data.html) - Load and save OSCAL documents in any format +- [Resolving Profiles](guides/resolving-profiles.html) - Resolve profiles to produce flattened catalogs +- [Validating with Constraints](guides/validating-with-constraints.html) - Validate OSCAL content diff --git a/src/site/resources/css/custom.css b/src/site/resources/css/custom.css index 2c3aa8f8..f03f5258 100644 --- a/src/site/resources/css/custom.css +++ b/src/site/resources/css/custom.css @@ -19,4 +19,11 @@ footer { code { color: #5f879b; +} + +/* Fix table header alignment - flexmark doesn't apply alignment to cells */ +/* Use higher specificity to override Bootstrap's .table th styles */ +.table th, +table th { + text-align: left !important; } \ No newline at end of file diff --git a/src/site/site.xml b/src/site/site.xml index 1090b479..12877b75 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -55,6 +55,20 @@ + + + + + + + + + + + + + +