Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
b71208b
chore: Refactor AsyncJobManager
rsenden May 6, 2026
0fc9066
chore: Refactor FcliExecutionContext, add transient session support
rsenden May 6, 2026
a9199a0
chore: Add support for transient session descriptor
rsenden May 6, 2026
2cb0b83
chore: Add HTTP MCP Server config
rsenden May 6, 2026
5cb6e91
chore: Implement MCP server (incomplete)
rsenden May 6, 2026
60667e3
chore: Implement HTTP MCP server auth handling
rsenden May 8, 2026
2184be9
chore: Code improvements
rsenden May 8, 2026
c380c99
chore: Move commands, improve implementation
rsenden May 8, 2026
56e96d2
chore: Add functional tests
rsenden May 8, 2026
2c6fcaf
chore: Various improvements
rsenden May 8, 2026
7cf377b
chore: Update usage help
rsenden May 8, 2026
e4ae4ef
chore: Refactor execution state management
rsenden May 8, 2026
e7fdf38
chore: Improvements, fixes
rsenden May 11, 2026
fe28931
ftest: Fix functional test execution
rsenden May 11, 2026
d6f09f0
chore: Fixes & improvements
rsenden May 11, 2026
0c4fb1c
ftest: Various fixes
rsenden May 11, 2026
88111e9
chore: Refactor FoD attribute handling, remove static caches
rsenden May 13, 2026
48bbf35
chore: Minor code improvements
rsenden May 13, 2026
797bcd5
chore: Progress writer stream improvements
rsenden May 13, 2026
6d97354
Revert "chore: Refactor FoD attribute handling, remove static caches"
rsenden May 13, 2026
17eb69e
Merge remote-tracking branch 'origin/dev/v3.x' into feat/v3.x/http-mcp
rsenden May 13, 2026
8762f94
chore: Refactor FoD attribute handling, remove static caches (re-impl…
rsenden May 13, 2026
31c792f
chore: Update Copilot instructions
rsenden May 14, 2026
74e0b84
chore: Null-safe handling
rsenden May 14, 2026
717a2ec
docs: Update JavaDoc, Copilot instructions
rsenden May 14, 2026
d649568
chore: Isolation state expiry, SSC/FoD token validation
rsenden May 14, 2026
91b904c
chore: Properly isolate and clean up stored variables
rsenden May 14, 2026
d00167e
chore: Update sample MCP HTTP config files
rsenden May 14, 2026
92e5963
chore: Update thread defaults & descriptions
rsenden May 14, 2026
1867138
chore: Set thread pool request executor
rsenden May 18, 2026
0adb710
chore: Restructure config, add TLS, bind address, request size
rsenden May 18, 2026
2a3bc93
chore: Improve thread safety; create new descriptor instead of updati…
rsenden May 18, 2026
5f4fd26
chore: Per-request log mask context
rsenden May 18, 2026
0b54ded
ftest: Fix MCP server config file structure
rsenden May 18, 2026
defb987
feat: Add `fcli agent extensions install|update|status` commands
rsenden May 20, 2026
517931a
chore: Major refactoring (output, tool-definitions, new commands)
rsenden May 21, 2026
e6f3196
fix: `fcli sc-sast sensor list`: Include full sensor details independ…
rsenden May 21, 2026
b8df964
Fix renamed agent mcp → ai-assist mcp command references, add AiAssis…
Copilot May 21, 2026
331240e
Merge pull request #1010 from fortify/copilot/featv3xagent-skills-fix…
rsenden May 21, 2026
25f8d07
chore: use deprecated fcli util mcp-server start in MCPServerImportSp…
Copilot May 21, 2026
adf51d6
Merge pull request #1012 from fortify/copilot/replace-deprecated-comm…
rsenden May 21, 2026
b28f9bb
chore: Various fixes & improvements
rsenden May 22, 2026
66d4e17
chore: Refactor ai-assist extensions command structure
rsenden May 22, 2026
d814551
chore: Use content-specific descriptors
rsenden May 22, 2026
9ce334c
chore: Add uninstall --dir option
rsenden May 22, 2026
408dd30
chore: Rename methods to indicate resource management requirement
rsenden May 22, 2026
b1967d4
fix: `fcli tool definitions update`: Ignore spurious intermediate dir…
rsenden May 23, 2026
e8cd4c8
chore: Updates based on review
rsenden May 23, 2026
ba743ce
chore: Fixes & improvements
rsenden May 24, 2026
39983d3
chore: Various fixes & improvements
rsenden May 24, 2026
1becd84
ci: Avoid duplicate workflow runs
rsenden May 24, 2026
5a45acd
ci: Avoid duplicate workflow runs (improvement)
rsenden May 24, 2026
ddf38e8
ci: Fix workflow permissions
rsenden May 24, 2026
043dd89
ci: Workflow improvements/fixes
rsenden May 24, 2026
999110a
chore: Improve glob matching
rsenden May 24, 2026
28a32a3
chore: Improve code quality
rsenden May 24, 2026
12ebad5
refactor: ai-assist helper classes for better separation of concerns
rsenden May 24, 2026
265f1da
fix: fcli MCP/RPC servers: Fix background job race condition
rsenden May 24, 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
50 changes: 36 additions & 14 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Fcli is a modular CLI tool for interacting with Fortify products (FoD, SSC, Scan
- **Validation:** Use `get_errors` tool first, then full Gradle build to catch warnings
- **Testing:**
- Unit tests: `src/test`; command structure validated in `FortifyCLITest`
- Functional tests: `fcli-core/fcli-functional-test` module; run with `./gradlew :fcli-core:fcli-functional-test:test`
- Functional tests: `fcli-other/fcli-functional-test` module; run with `./gradlew :fcli-other:fcli-functional-test:ftest`

## Code Conventions

Expand All @@ -29,19 +29,41 @@ Fcli is a modular CLI tool for interacting with Fortify products (FoD, SSC, Scan
- Short methods (~20 lines max); extract helpers or use Streams for clarity
- No change-tracking comments (e.g., "New ...", "Updated ..."); only explanatory comments when code is complex

## Maintaining Instructions

**If you detect discrepancies between these instructions and the actual implementation**, or discover patterns/features not documented here:

1. **Notify the user** about the discrepancy or missing documentation
2. **Suggest specific updates** to the relevant instruction file(s)
3. **Verify against current code** before making changes based on outdated instructions

This applies to:
- Main instructions (this file)
- Specific instruction files in `.github/instructions/`
- Examples that no longer match current patterns
- Missing documentation for new features or utilities
## Agent Behavior

**Think and work as a senior/expert developer** on every task:
- Apply SOLID principles, proper separation of concerns, and established design patterns
- Treat code quality and security as non-negotiable, not optional extras
- Never guess or make assumptions about how existing code works — analyze first:
1. Use `semantic_search`, `grep_search`, and `read_file` to trace through related code
2. Find all callers of anything you change (`vscode_listCodeUsages`)
3. Understand the design intent before writing a single line
- Identify and use the most appropriate existing abstraction instead of creating a new one
- If a request is ambiguous, surface the design trade-offs and ask; do not silently pick one

## Self-Learning Instructions

**Automatically update these instruction files** when you learn or implement something new that would help in future sessions. Do not just notify the user — make the edit as part of completing the task.

Update the file that best scopes the knowledge:
- `copilot-instructions.md` — project-wide workflow facts and agent behavior rules
- `.github/instructions/java.instructions.md` — Java patterns, architecture, APIs
- `.github/instructions/style.instructions.md` — naming, formatting, AI assistant rules
- `.github/instructions/utilities.instructions.md` — utility class discoveries

Rules for self-updating:
- Only add content that is **reusable and non-obvious** (things a smart developer wouldn't know without reading the source)
- Keep additions **concise** — bullet points and short examples, not prose explanations; no introductory paragraphs
- A method signature + one-liner purpose is enough; omit obvious behaviour
- Correct or remove instructions that turn out to be wrong
- Never duplicate existing content; prefer extending an existing section
- Verify the change compiles/is consistent before updating instructions

**If you detect discrepancies** between these instructions and the actual implementation:
1. **Fix the instruction** to match reality (or fix the code if it's a bug)
2. **Verify against current code** before proceeding — never rely on stale instructions

This applies to all instruction files in `.github/instructions/`.

## Context-Specific Instructions

Expand Down
49 changes: 46 additions & 3 deletions .github/instructions/java.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ applyTo: 'fcli/**/*.java'

**If you detect discrepancies** between these instructions and the actual implementation, or discover patterns/features not documented here:

1. **Notify the user** about the discrepancy or missing documentation
2. **Suggest specific updates** to this instruction file
3. **Verify against current code** before proceeding with changes based on potentially outdated instructions
1. **Automatically update this file** to reflect the correct pattern or add the missing information
2. **Verify against current code** before documenting — never guess at intent
3. Only add content that is **reusable and non-obvious**; keep additions concise

## Architecture Overview

Expand Down Expand Up @@ -129,6 +129,49 @@ try { parse(json); } catch (JsonProcessingException e) { throw new FcliTechnical
default -> throw new FcliBugException("Unexpected status: "+status);
```

## Design Patterns in fcli

Fcli uses several established patterns consistently. Before creating new abstractions, identify whether one of these already covers your need:

### Template Method
Abstract base classes define the algorithm skeleton; subclasses fill in the steps.
- `AbstractRunnableCommand.call()` orchestrates option validation → execution → output
- `AbstractSessionLoginCommand` handles session lifecycle; subclasses provide credentials and connection logic
- **Rule:** Override the narrowest hook method, not the full template

### Strategy (via Interfaces + Mixins)
Behavior is injected at the call-site rather than baked into the consumer.
- `IOutputConfigSupplier` lets commands declare default format/columns without knowing the writer
- `IRecordWriter` implementations (CSV, JSON, table, …) are selected at runtime by `RecordWriterFactory`
- `UnirestInstanceSupplierMixin` injects an HTTP client configured for the active session
- **Rule:** When a class needs pluggable behavior, define an interface and inject it via `@Mixin` or constructor; avoid `if/else` type-switches

### Factory / Registry
Creation and type selection are centralized.
- `RecordWriterFactory` enum maps output format → writer implementation
- `OutputHelperMixins` inner classes pair a command name with its default output config
- **Rule:** Add new variants by extending the factory/enum, not by modifying consumer code (Open/Closed)

### Composite (Command Tree)
Picocli's subcommand tree is a composite: container commands own leaf commands.
- **Rule:** Container commands contain only `@Command` metadata and subcommand registration; zero business logic

### Mixin / Decorator (Picocli `@Mixin`)
Cross-cutting CLI concerns (output, session, pagination, …) are composed in, not inherited.
- `OutputHelperMixins.List` brings `--output`, `--query`, `--store` to any list command
- **Rule:** Prefer mixins over base-class inheritance for optional/composable features

### Builder (Unirest fluent API)
HTTP requests are assembled step-by-step.
- Always use `headerReplace()` not `header()` / `accept()` / `contentType()` (see Unirest section)

### Separation of Concerns checklist
Before committing, verify:
- Command class: only option fields, `@Mixin` injections, and a short `call()` that delegates
- Helper/service class: business logic; no Picocli annotations, no direct I/O
- Writer/formatter class: output shaping only; no HTTP calls, no business logic
- Session descriptor: data only (record or simple POJO); no network code

## Common Utility Classes

The `fcli-common` module provides utility classes in `com.fortify.cli.common.util` for common operations. Always prefer these over direct JDK/third-party equivalents. See the utilities guide for complete documentation.
44 changes: 36 additions & 8 deletions .github/instructions/style.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ applyTo: 'fcli/**/*'

**If you detect discrepancies** between these instructions and the actual codebase patterns, or discover conventions not documented here:

1. **Notify the user** about the discrepancy or missing documentation
2. **Suggest specific updates** to this instruction file
3. **Consider whether the discrepancy represents an intentional exception** (clarity over rules) or an outdated instruction
1. **Automatically update this file** to reflect the correct pattern
2. **Consider whether the discrepancy represents** an intentional exception (clarity over rules) or an outdated instruction
3. **Verify against current code** before documenting — never guess at intent

## General

Expand All @@ -27,6 +27,18 @@ applyTo: 'fcli/**/*'
- Use Streams for clear transformations/filtering, but favor simple for-loops if they are more readable or avoid unnecessary allocations.
- Use `Optional` sparingly (avoid in hot inner loops or for simple nullable fields inside DTOs).

## Lombok Usage

Always prefer Lombok annotations over hand-written boilerplate. Key rules beyond the obvious:

- **`@Getter(lazy = true)`** — for expensive fields computed once on first access (thread-safe).
- **`@Getter(value = AccessLevel.PRIVATE)`** — restrict getter visibility; combine with `@Accessors(fluent = true)` for fluent data-holder APIs.
- **`@Builder` + `@RequiredArgsConstructor(access = AccessLevel.PRIVATE)`** — enforces construction via builder only; use `@Builder.Default` for non-null field defaults.
- **Jackson-deserialized descriptors (`@Reflectable`)** — do NOT add `@Builder`; use `@NoArgsConstructor` + `@Data` / `@Getter` (Jackson requires a no-arg constructor).
- **`@Data` with inheritance** — always add `@EqualsAndHashCode(callSuper = true)` explicitly.
- **`@Slf4j`** — preferred for new code; older code uses `LoggerFactory.getLogger(getClass())`.
- **Avoid** `@Value` (use Java `record`), `@With`, `@SuperBuilder` — not currently in use.

## Imports & Formatting

- Always use explicit imports; avoid wildcard imports.
Expand Down Expand Up @@ -77,13 +89,29 @@ applyTo: 'fcli/**/*'
- Record producers should remain side-effect free except for streaming output records.
- Avoid static mutable state; prefer instance-level control (see recent refactor removing static collectors).

## Security Mindset

Apply OWASP principles defensively — even in a CLI tool that processes data from trusted sources:

- **Injection:** Although injecting user input into shell commands, file paths, SpEL/template expression is fairly common in fcli, always verify the potential security impact and apply proper safeguards if appropriate.
- **Isolation:** Avoid static mutable state that could be manipulated across command executions; prefer instance-level state or thread confinement. For multi-user server scenarios like MCP HTTP server, ensure proper data isolation.
- **Sensitive data exposure:** Don't log or print credentials, tokens, or secrets; use `saveSecuredFile()` / `readSecuredFile()` for persisted session credentials, and make sure that log masking is applied (see `LogMaskHelper`)
- **Path traversal:** Always resolve user-supplied paths against an expected root; use `FileUtils`'s zip-slip-protected extraction methods rather than raw `ZipEntry.getName()`
- **Deserialization:** Prefer Jackson with explicit type constraints; avoid `ObjectMapper.enableDefaultTyping()` or polymorphic `@JsonTypeInfo` with user-controlled type names
- **Dependency hygiene:** Don't add new dependencies without reviewing their transitive impact; prefer well-maintained libraries already on the classpath

## AI Assistant Expectations

- Before large edits: scan related files (search by symbol) to avoid breaking contracts.
- After edits: run compile, address warnings if feasible.
- Never introduce commented-out code blocks; remove instead.
- Provide incremental, minimal diffs; do not reformat unrelated code.
- If refactoring signature changes, update all usages in same change.
- **Analyze before implementing:** Trace through existing abstractions with `semantic_search`, `grep_search`, and `vscode_listCodeUsages` before writing a single line. Understand the design intent, not just the surface syntax.
- **Reuse, don't reinvent:** Identify the most appropriate existing base class, mixin, or utility before creating new abstractions. Fcli has rich shared infrastructure — use it.
- **Apply SOLID principles:** Single Responsibility (each class does one thing), Open/Closed (extend via interfaces/abstractions, not modification), Liskov Substitution (subtypes must honor contracts), Interface Segregation (small focused interfaces), Dependency Inversion (depend on abstractions).
- **Separation of concerns:** Commands parse options and orchestrate; helpers/services contain business logic; writers handle output. Don't embed formatting in commands or business logic in writers.
- **Before large edits:** Scan related files (search by symbol) to avoid breaking contracts.
- **After edits:** Run `get_errors`, address any issues; build with Gradle if appropriate.
- **Never introduce commented-out code blocks;** remove instead.
- **Provide incremental, minimal diffs;** do not reformat unrelated code.
- **If refactoring changes a signature,** update all usages in the same change.
- **Self-update these instructions** when you discover a pattern, pitfall, or convention not yet documented here — make the edit immediately as part of the task.

## Pull Request Hygiene

Expand Down
51 changes: 48 additions & 3 deletions .github/instructions/utilities.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ The `fcli-common` module provides utility classes in `com.fortify.cli.common.uti

**If you discover utility classes, methods, or patterns** in `fcli-common/src/main/java/com/fortify/cli/common/util/` that are not documented here, or if documented utilities appear outdated/incorrect:

1. **Notify the user** about the missing or outdated documentation
2. **Suggest specific additions/updates** to this section
3. **Verify the utility's purpose and usage** in the codebase before documenting
1. **Automatically update this file** with the correct or missing documentation
2. **Verify the utility's purpose and usage** in the codebase before documenting
3. Keep additions concise — method signatures and a one-liner purpose are enough

## Environment & Configuration

Expand Down Expand Up @@ -130,6 +130,51 @@ Global debug flag.

- `isDebugEnabled()`, `setDebugEnabled()`

## Execution Context & Isolation

These classes are in `com.fortify.cli.common.cli.util`, not `util/`, but are essential infrastructure.

### `FcliExecutionContext`
Per-invocation execution frame. Always created/closed via `FcliExecutionContextHolder`.

- Holds `UnirestContext` (fresh per external entry point), `FcliActionState`, and `FcliIsolationScope`
- Use try-with-resources: `try (var frame = FcliExecutionContextHolder.pushNew()) { ... }`
- `run.fcli` sub-commands reuse the parent frame; do not call `pushNew()` inside them

### `FcliExecutionContextHolder`
Thread-local stack for nested execution frames.

- `pushNew()` — creates a completely fresh context; use at top-level entry points (CLI, MCP, RPC)
- `push(ctx)` — pushes an existing context (e.g. child frames that share the parent's isolation scope)
- `current()` — returns the active context; throws if none is present
- Closing the returned `ContextFrame` pops and closes the context automatically

### `FcliIsolationScope`
Groups related invocations under the same auth/session boundary.

- Plain CLI: one new scope per invocation
- MCP stdio / RPC server: one scope for the server lifetime (all tool calls share sessions)
- MCP HTTP server: one scope **per authenticated identity** (full isolation between clients)
- Holds `transientSessionDescriptors` — in-memory sessions that are not persisted to disk

### `FcliActionState`
Mutable bag of `global.*` action variables.

- Each external CLI call and non-imported MCP/RPC tool call gets a **fresh** `FcliActionState` (no cross-call leakage)
- Imported action functions in **MCP stdio / RPC stdio** share one `FcliActionState` instance for the server lifetime
- Imported action functions in **MCP HTTP server** get a per-credentials-hash `FcliActionState` stored in the per-auth-scope `FcliIsolationScope` — persists across calls from the same user, isolated from other users
- `run.fcli` sub-commands inherit and mutate the parent's state

## Log Masking

### `LogMaskHelper` (`com.fortify.cli.common.log`)
Singleton for registering values/patterns to mask in logs and stdio.

- `LogMaskHelper.INSTANCE.registerValue(maskAnnotation, source, value)` — register a sensitive value using `@MaskValue` annotation semantics
- `registerPattern(level, pattern, replacement, types...)` — register a regex pattern for masking
- Session credentials are registered automatically by `AbstractSessionHelper`; CLI option values are registered by `FcliExecutionStrategy` when the field carries `@MaskValue`
- Do NOT log or print secrets directly; annotate sensitive option fields with `@MaskValue` instead

## Usage Principles

1. **Prefer fcli utilities**: Use `EnvHelper.env()` over `System.getenv()`, `FcliDataHelper` over raw `Files` API for fcli data
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
name: Build and release
run-name: "Build and release (${{ github.event_name }})"
on:
workflow_dispatch:
pull_request:
Expand Down Expand Up @@ -26,8 +27,13 @@ env:
graal_java_version: 21

jobs:
check-duplicate-run:
uses: fortify/.github/.github/workflows/check-duplicate-run.yml@main

create-release:
name: create-release
needs: check-duplicate-run
if: needs.check-duplicate-run.outputs.should_skip != 'true'
runs-on: ubuntu-latest
permissions:
contents: write
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/fortify-analysis.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
name: Fortify on Demand Scan
run-name: "Fortify on Demand Scan (${{ github.event_name }})"

on:
workflow_dispatch:
Expand All @@ -13,7 +14,12 @@ permissions:
security-events: write

jobs:
check-duplicate-run:
uses: fortify/.github/.github/workflows/check-duplicate-run.yml@main

FoD-Scan:
needs: check-duplicate-run
if: needs.check-duplicate-run.outputs.should_skip != 'true'
uses: fortify/.github/.github/workflows/fortify-analysis.yml@main
with:
java-version: '17'
Expand Down
10 changes: 10 additions & 0 deletions fcli-core/fcli-ai-assist/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
plugins { id("fcli.module-conventions") }

dependencies {
val fodRef = project.findProperty("fcliFoDRef") as String
val sscRef = project.findProperty("fcliSSCRef") as String
val toolRef = project.findProperty("fcliToolRef") as String
implementation(project(fodRef))
implementation(project(sscRef))
implementation(project(toolRef))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2021-2026 Open Text.
*
* The only warranties for products and services of Open Text
* and its affiliates and licensors ("Open Text") are as may
* be set forth in the express warranty statements accompanying
* such products and services. Nothing herein should be construed
* as constituting an additional warranty. Open Text shall not be
* liable for technical or editorial errors or omissions contained
* herein. The information contained herein is subject to change
* without notice.
*/
package com.fortify.cli.ai_assist._main.cli.cmd;

import static com.fortify.cli.common.cli.util.FcliModuleCategories.UTIL;

import com.fortify.cli.ai_assist.extensions.cli.cmd.AiAssistExtensionsCommands;
import com.fortify.cli.ai_assist.mcp.cli.cmd.AiAssistMCPCommands;
import com.fortify.cli.common.cli.cmd.AbstractContainerCommand;
import com.fortify.cli.common.cli.util.FcliModuleCategory;

import picocli.CommandLine.Command;

@FcliModuleCategory(UTIL)
@Command(
name = "ai-assist",
aliases = {"ai"},
resourceBundle = "com.fortify.cli.ai_assist.i18n.AiAssistMessages",
subcommands = {
AiAssistExtensionsCommands.class,
AiAssistMCPCommands.class
}
)
public class AiAssistCommands extends AbstractContainerCommand {}
Loading
Loading