Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Boostrap oscal generator and subcommand #194

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open

Conversation

puerco
Copy link
Member

@puerco puerco commented Feb 19, 2025

This command initializes the first oscal output and generator for the base line data.

This lets us generate a first draft of an OSCAL catalog to start testing and iterating on the format.

Here's a sample of the output for feedback
https://gist.github.com/puerco/e4a18354c281d45fc900cfb626adf286

/cc @brandtkeller

Signed-off-by: Adolfo García Veytia (Puerco) adolfo.garcia@uservers.net

Comment on lines 64 to 69
Parts: &[]oscal.Part{
{
ID: strings.TrimPrefix(criteria.ID, "OSPS-") + "_level",
Name: "maturity-level",
Prose: fmt.Sprintf("%d", criteria.MaturityLevel),
},
Copy link

@xee5ch xee5ch Feb 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would strongly recommend using OSCAL props: [] in the JSON data format) over parts for this.

Here is the official OSCAL documentation for that in the catalog model. They nest inside the control similarly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback, love the suggestion! I've pushed a new commit moving maturity level to a property. Check out the new output here
https://gist.github.com/puerco/e4a18354c281d45fc900cfb626adf286


enc := json.NewEncoder(w)
enc.SetIndent("", " ")
if err := enc.Encode(catalog); err != nil {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will produce the file for all of the content within the catalog model - but if we look at a catalog example we need the catalog object key included for validity of the model.

This can be done a number of ways. Typically we use the complete schema to reference the catalog and encode the complete schema.

go-oscal has a CLI with a validate command that can perform schema validation of models if that is helpful.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah thanks for the tip! I've wrapped the doc into a "catalog" label and fixed the validation:

go run . validate --input-file ../ossf/security-baseline/cmd/oscal.json  
go-oscal: 2025/02/20 19:05:13 Successfully validated ../ossf/security-baseline/cmd/oscal.json is valid OSCAL version 1.1.3 catalog

)

const (
VersionOSPS = "devel"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So how will this work when we release a version? Currently, the SOP is to copy the rendered .md to a directory. How do we generate the OSCAL JSON with the correct version strings?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is just "this current version" the issue at hand, @funnelfiasco, or you want to consider how you manage "the lifecycle of versions and say where this version is a series of versions" kind of perspective? OSCAL can encode both, not sure how you all want to handle it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My concern is that including a link to the "devel" version of a control may not give the right information if the file was generated against a previous version of the OSPS Baseline. It's not clear to me what @puerco intends to do with the generated json. Is it something we'll publish alongside the versioned baseline files? Is it something that we expect people to generate ad hoc if they need it? Something else?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK cool. So there is simple and complex ways to handle this in OSCAL, I am not sure which one suits the group best. Each OSCAL model, of course the beloved catalog, uses the same //metadata assembly and that can have zero, one or many revisions. Now you could "reinvent all of git" (and I say that sarcastically, who would do that?) and enumerate every revision. But there is another thing you could do here, if it suits you all. You see the docs talk about this:

Constraint (1)

allowed values for link/@rel

The value may be locally defined, or one of the following:

canonical: The link identifies the authoritative location for this resource. Defined by RFC 6596.
alternate: The link identifies an alternative location or format for this resource. Defined by the HTML Living Standard
predecessor-version: This link identifies a resource containing the predecessor version in the version history. Defined by RFC 5829.
successor-version: This link identifies a resource containing the predecessor version in the version history. Defined by RFC 5829.
version-history: This link identifies a resource containing the version history of this document. Defined by RFC 5829.

So why do I bring this up? You could decide to keep the version at devel for now or figure out some other ID. And you can point to the version-history to say where all releases are, before or after you release them. Something like so:

---
catalog:
  uuid: 394c88cf-07c0-4380-899b-053810d8b21e
  version: devel # or maybe something different?
  oscal-version: 1.1.3
  last-modified: 2025-02-20T03:43:57Z
  published:      2025-02-20T03:43:57Z
  metadata:
    revisions:
    - title: OpenSSF Security Baseline Releases
      links:
      - rel: version-history
        href: https://github.com/ossf/security-baseline/releases

You could also do something different with predecessor-version or successor-version once you have a release or two under your belt, but I find that more complicated than this approach with that @rel type. That's why it got added. As for devel there are some other things you could try but I will say my goto that is problematic is a git SHA1 hash? Why not that? Well, because if you want to hash the content and make that the version of the catalog before a release, it is kind of a recursive hell where you need to answer "how do I embed the hash of the content into the content I am hashing?" so I am out of good pre-release ideas, but this facility does give you what you need for the "link to previous versions of the baseline" scenario maybe?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is the version string of the baseline the generator renders. This will be automatically picked up from the relases once we have them. For now it is hardcoded to"devel". I'll work on the tagging machinery once we cut our second release.

Thanks for the version-history @xee5ch! I'll make sure we generate those once we start releasing the oscal files as part of the releases .

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, I thought you would appreciate that. From NIST or other community adopters, you are one of the few to take this tact and it is the smart play. Happy to see it!

Comment on lines 64 to 69
Props: &[]oscal.Property{
{
Name: "maturity-level",
UUID: catalogUUID,
Value: fmt.Sprintf("%d", criteria.MaturityLevel),
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the update here to use prop, but another minor recommendation given extending and customizing data, it might make sense to namespace (with ns) that maturity-level has a specific meaning in the context of OpenSSF and if other organizations have a similar prop, they do not "collide" with its use or downstream code assumes some other group's use of the term applies here. The go-oscal library seems to support this, so I'd recommend picking a ns URL to use in props and other things and sticking to it through the codebase for namespacing per the guidance.

I am not saying it has to be that specific value, but just giving you an idea of where I am going with it.

Suggested change
Props: &[]oscal.Property{
{
Name: "maturity-level",
UUID: catalogUUID,
Value: fmt.Sprintf("%d", criteria.MaturityLevel),
},
Props: &[]oscal.Property{
{
Ns: "http://openssf.org/ns/oscal",
Name: "maturity-level",
UUID: catalogUUID,
Value: fmt.Sprintf("%d", criteria.MaturityLevel),
},

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took out the maturity level for now as I need to introduce some changes to the new schema to handle them properly.

Comment on lines 87 to 91
pts := append(*newCtl.Parts, oscal.Part{
ID: strings.TrimPrefix(criteria.ID, "OSPS-") + "_implementation",
Name: "implementation",
Prose: strings.TrimSpace(criteria.Implementation),
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would recommend per the OSCAL docs on /catalog/parts/@name to change this to example.

Suggested change
pts := append(*newCtl.Parts, oscal.Part{
ID: strings.TrimPrefix(criteria.ID, "OSPS-") + "_implementation",
Name: "implementation",
Prose: strings.TrimSpace(criteria.Implementation),
})
pts := append(*newCtl.Parts, oscal.Part{
ID: strings.TrimPrefix(criteria.ID, "OSPS-") + "_example",
Name: "example",
Prose: strings.TrimSpace(criteria.Implementation),
})

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one was also deprecated with the new schema

This command initializes the first oscal output and
generator for the baseline data.

Signed-off-by: Adolfo García Veytia (Puerco) <adolfo.garcia@uservers.net>
Signed-off-by: Adolfo García Veytia (Puerco) <adolfo.garcia@uservers.net>
Signed-off-by: Adolfo García Veytia (Puerco) <adolfo.garcia@uservers.net>
@puerco puerco force-pushed the oscal branch 2 times, most recently from cc8786c to 7988aa8 Compare February 21, 2025 01:08
@puerco
Copy link
Member Author

puerco commented Feb 21, 2025

@xee5ch I see that the NIST namespace url (https://csrc.nist.gov/ns/oscal) does not return anything. Does this mean that the IRI is just used as a label?

Going through the docs it seems that the namespace does not really need to be defined anywhere, other linked data formats namespace with an URI/IRI but also expect a definition file (for example the Context in JSONLD).

I guess we are fine just defining our namespace and not really worrying about writing a definition?

@xee5ch
Copy link

xee5ch commented Feb 21, 2025

Correct it is just a label.

Signed-off-by: Adolfo García Veytia (Puerco) <adolfo.garcia@uservers.net>
@puerco
Copy link
Member Author

puerco commented Feb 21, 2025

OK, I pushed commits with updates for the new schema and to fix the validation with @defenseunicorns go-oscal.

The new output is published here: https://gist.github.com/puerco/e4a18354c281d45fc900cfb626adf286

Comment on lines +70 to +71
Name: "recomemendation",
Ns: OpenSSFNS,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am going to try and dig into this over the weekend, but in the short-term you may want to correct the spelling of the part name.

Suggested change
Name: "recomemendation",
Ns: OpenSSFNS,
Name: "recommendation",
Ns: OpenSSFNS,

I would say a higher-level issue from using the maintained oscal-cli community fork is that there may be specific OSCAL rules that say a part should have a certain name if it is in the "default" core NIST OSCAL namespace. Weirdly, yours is not. You did it right. However, the error still persists in the tooling, so I will look into that; it might be a bug on that end so you can safely ignore it. :-)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually it seems it is related to the NIST's definition in the OSCAL model, not the tooling. More to follow after usnistgov/OSCAL#2112 is resolved and released in a new model version. You helped find an interesting edge case!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed it too, it also seems that it is ignoring namespaced names.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants