Skip to content

Commit

Permalink
update api for resolve and convert
Browse files Browse the repository at this point in the history
  • Loading branch information
wandmagic committed Oct 4, 2024
1 parent e875afc commit 17a6aba
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import java.util.LinkedList
import java.util.Deque
import gov.nist.secauto.oscal.tools.cli.core.commands.ConvertCommand
import gov.nist.secauto.oscal.tools.cli.core.commands.ValidateCommand
import gov.nist.secauto.oscal.tools.cli.core.commands.ResolveCommand

open class OscalCommandExecutor(
protected val command: String,
Expand All @@ -32,7 +33,8 @@ open class OscalCommandExecutor(
protected val logger: Logger = LogManager.getLogger(this::class.java)
protected open val commands: Map<String, () -> ICommand> = mapOf(
"validate" to ::ValidateCommand,
"convert" to ::ConvertCommand
"convert" to ::ConvertCommand,
"resolve-profile" to ::ResolveCommand
)

protected open fun getBindingContext(): IBindingContext {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import java.util.UUID
import io.vertx.ext.web.handler.StaticHandler
import io.vertx.ext.web.handler.BodyHandler
import gov.nist.secauto.metaschema.cli.processor.ExitStatus
import gov.nist.secauto.metaschema.cli.processor.MessageExitStatus
import io.vertx.ext.web.Router
import io.vertx.ext.web.RoutingContext
import io.vertx.ext.web.openapi.OpenAPILoaderOptions
Expand Down Expand Up @@ -55,6 +56,8 @@ class OscalVerticle : CoroutineVerticle() {

routerBuilder.operation("validateUpload").handler { ctx -> handleValidateFileUpload(ctx) }
routerBuilder.operation("validate").handler { ctx -> handleValidateRequest(ctx) }
routerBuilder.operation("resolve").handler { ctx -> handleResolveRequest(ctx) }
routerBuilder.operation("convert").handler { ctx -> handleConvertRequest(ctx) }
val router = routerBuilder.createRouter()
router.route("/*").handler(StaticHandler.create("webroot"))
return router
Expand Down Expand Up @@ -110,7 +113,7 @@ class OscalVerticle : CoroutineVerticle() {
private fun handleValidateRequest(ctx: RoutingContext) {
launch {
try {
logger.info("Handling CLI request")
logger.info("Handling Validate request")
val encodedContent = ctx.queryParam("content").firstOrNull()
val content = encodedContent?.let { URLDecoder.decode(it, StandardCharsets.UTF_8.name()) }
if (content != null) {
Expand All @@ -129,6 +132,64 @@ class OscalVerticle : CoroutineVerticle() {
}
}
}
private fun handleResolveRequest(ctx: RoutingContext) {
launch {
try {
logger.info("Handling Resolve request")
val encodedContent = ctx.queryParam("content").firstOrNull()
val content = encodedContent?.let { URLDecoder.decode(it, StandardCharsets.UTF_8.name()) }
val acceptHeader = ctx.request().getHeader("Accept")
val format = mapMimeTypeToFormat(acceptHeader)

if (content != null) {
// Use async for parallelism
val result = async {
executeCommand(parseCommandToArgs("resolve-profile $content --to=$format"))
}.await() // Wait for the result of the async execution
logger.info(result.second)
sendSuccessResponse(ctx, result.first, result.second)
} else {
sendErrorResponse(ctx, 400, "content parameter is missing")
}
} catch (e: Exception) {
logger.error("Error handling CLI request", e)
sendErrorResponse(ctx, 500, "Internal server error")
}
}
}
private fun mapMimeTypeToFormat(mimeType: String?): String {
return when (mimeType) {
"application/json" -> "JSON"
"application/xml" -> "XML"
"application/x-yaml" -> "YAML"
else -> "JSON" // Default to JSON if no valid MIME type is provided
}
}
private fun handleConvertRequest(ctx: RoutingContext) {
launch {
try {
logger.info("Handling Convert request")
val encodedContent = ctx.queryParam("content").firstOrNull()
val content = encodedContent?.let { URLDecoder.decode(it, StandardCharsets.UTF_8.name()) }
val acceptHeader = ctx.request().getHeader("Accept")
val format = mapMimeTypeToFormat(acceptHeader)

if (content != null) {
// Use async for parallelism
val result = async {
executeCommand(parseCommandToArgs("convert $content --to=$format"))
}.await() // Wait for the result of the async execution
logger.info(result.second)
sendSuccessResponse(ctx, result.first, result.second)
} else {
sendErrorResponse(ctx, 400, "content parameter is missing")
}
} catch (e: Exception) {
logger.error("Error handling CLI request", e)
sendErrorResponse(ctx, 500, "Internal server error")
}
}
}

private fun parseCommandToArgs(command: String): List<String> {
return command.split("\\s+".toRegex()).filter { it.isNotBlank() }
Expand Down Expand Up @@ -166,20 +227,20 @@ class OscalVerticle : CoroutineVerticle() {
if(mutableArgs.contains(("-o"))){
throw Error("Do not specify sarif file")
}
if(!mutableArgs.contains(("--sarif-include-pass"))){
mutableArgs.add("--sarif-include-pass")
if (mutableArgs[0]=="validate"){
if(!mutableArgs.contains(("--sarif-include-pass"))){
mutableArgs.add("--sarif-include-pass")
}
mutableArgs.add("-o")
}
mutableArgs.add("-o")
mutableArgs.add(sarifFilePath)

val oscalCommandExecutor = OscalCommandExecutor(command, mutableArgs)
val exitStatus = oscalCommandExecutor.execute()
logger.info("Command execution completed with status: $exitStatus")

// Check if SARIF file was created
if (!File(sarifFilePath).exists()) {
logger.warn("SARIF file not generated. Creating a basic SARIF file with error message.")
val basicSarif = createBasicSarif("Error")
val basicSarif = createBasicSarif("code:"+exitStatus.exitCode.toString())
File(sarifFilePath).writeText(basicSarif)
}

Expand Down

This file was deleted.

2 changes: 1 addition & 1 deletion src/main/resources/log4j2.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
<Filters>
<!-- Only allow WARN level and above -->
<ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY"/>
<ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
</Console>
</Appenders>
Expand Down
100 changes: 82 additions & 18 deletions src/main/resources/webroot/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,34 @@ openapi: 3.0.0
info:
title: OSCAL SERVER
version: 1.0.0
description: API for validating OSCAL Content
description: API for validating, resolving, and converting OSCAL Content

paths:
/validate:
get:
operationId: validate
summary: Validate Oscal remote document
summary: Validate remote OSCAL document
parameters:
- in: query
name: content
required: true
schema:
type: string
example: https://raw.githubusercontent.com/GSA/fedramp-automation/refs/heads/develop/src/validations/constraints/content/ssp-all-VALID.xml
description: Validate remote oscal document from URI
format: uri
description: URI of the remote OSCAL document to validate
example:
- https://raw.githubusercontent.com/wandmagic/fedramp-automation/refs/heads/feature/external-constraints/src/validations/constraints/content/ssp-all-VALID.xml
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/SarifResponse'
$ref: '#/components/responses/SarifResponse'
'400':
$ref: '#/components/responses/BadRequest'
'500':
$ref: '#/components/responses/InternalServerError'

post:
operationId: validateUpload
summary: Validate Oscal document from uploaded file
summary: Validate uploaded OSCAL document
requestBody:
required: true
content:
Expand All @@ -39,11 +39,47 @@ paths:
format: binary
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/SarifResponse'
$ref: '#/components/responses/SarifResponse'
'400':
$ref: '#/components/responses/BadRequest'
'500':
$ref: '#/components/responses/InternalServerError'

/resolve:
get:
operationId: resolve
summary: Resolve OSCAL local document
parameters:
- in: query
name: content
required: true
schema:
type: string
format: uri
description: Absolute Path of the local OSCAL document to resolve
responses:
'200':
$ref: '#/components/responses/OscalResponse'
'400':
$ref: '#/components/responses/BadRequest'
'500':
$ref: '#/components/responses/InternalServerError'

/convert:
get:
operationId: convert
summary: Convert OSCAL document
parameters:
- in: query
name: content
required: true
schema:
type: string
format: uri
description: URI of the remote OSCAL document to convert
responses:
'200':
$ref: '#/components/responses/OscalResponse'
'400':
$ref: '#/components/responses/BadRequest'
'500':
Expand All @@ -54,23 +90,51 @@ components:
SarifResponse:
type: object
properties:
runs:
type: array
version:
type: string
runs:
type: array
items:
type: object

OscalContent:
type: object
description: OSCAL content (structure depends on the specific OSCAL format)

Error:
type: object
properties:
error:
type: string

responses:
SarifResponse:
description: Successful SARIF response
content:
application/json:
schema:
$ref: '#/components/schemas/SarifResponse'

OscalResponse:
description: Successful OSCAL response
content:
application/json:
schema:
$ref: '#/components/schemas/OscalContent'
application/xml:
schema:
$ref: '#/components/schemas/OscalContent'
application/x-yaml:
schema:
$ref: '#/components/schemas/OscalContent'

BadRequest:
description: Bad request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'

InternalServerError:
description: Internal server error
content:
Expand Down

0 comments on commit 17a6aba

Please sign in to comment.