Skip to content

Commit

Permalink
Added support for extension reference lookup control
Browse files Browse the repository at this point in the history
extensions will not see any internal properties using `$ref` unless enabled using the new `ext-refs` global flag
  • Loading branch information
daveshanley committed Feb 2, 2025
1 parent 858b93e commit 9789cf5
Show file tree
Hide file tree
Showing 8 changed files with 68 additions and 49 deletions.
5 changes: 5 additions & 0 deletions cmd/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func GetDashboardCommand() *cobra.Command {
timeoutFlag, _ := cmd.Flags().GetInt("timeout")
hardModeFlag, _ := cmd.Flags().GetBool("hard-mode")
silent, _ := cmd.Flags().GetBool("silent")
extensionRefsFlag, _ := cmd.Flags().GetBool("ext-refs")

var err error
vacuumReport, specBytes, _ := vacuum_report.BuildVacuumReportFromFile(args[0])
Expand Down Expand Up @@ -102,6 +103,10 @@ func GetDashboardCommand() *cobra.Command {
config.AllowRemoteLookup = true
}

if extensionRefsFlag {
config.ExcludeExtensionRefs = true
}

specIndex = index.NewSpecIndexWithConfig(&rootNode, config)

specInfo = vacuumReport.SpecInfo
Expand Down
2 changes: 2 additions & 0 deletions cmd/language_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ IDE and start linting your OpenAPI documents in real-time.`,
hardModeFlag, _ := cmd.Flags().GetBool("hard-mode")
ignoreArrayCircleRef, _ := cmd.Flags().GetBool("ignore-array-circle-ref")
ignorePolymorphCircleRef, _ := cmd.Flags().GetBool("ignore-array-circle-ref")
extensionRefsFlag, _ := cmd.Flags().GetBool("ext-refs")

defaultRuleSets := rulesets.BuildDefaultRuleSetsWithLogger(logger)
selectedRS := defaultRuleSets.GenerateOpenAPIRecommendedRuleSet()
Expand Down Expand Up @@ -81,6 +82,7 @@ IDE and start linting your OpenAPI documents in real-time.`,
IgnoreArrayCircleRef: ignoreArrayCircleRef,
IgnorePolymorphCircleRef: ignorePolymorphCircleRef,
Logger: logger,
ExtensionRefs: extensionRefsFlag,
}

return languageserver.NewServer(Version, &lfr).Run()
Expand Down
27 changes: 15 additions & 12 deletions cmd/lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ func GetLintCommand() *cobra.Command {
timeoutFlag, _ := cmd.Flags().GetInt("timeout")
hardModeFlag, _ := cmd.Flags().GetBool("hard-mode")
noClipFlag, _ := cmd.Flags().GetBool("no-clip")
extensionRefsFlag, _ := cmd.Flags().GetBool("ext-refs")
ignoreArrayCircleRef, _ := cmd.Flags().GetBool("ignore-array-circle-ref")
ignorePolymorphCircleRef, _ := cmd.Flags().GetBool("ignore-polymorph-circle-ref")
ignoreFile, _ := cmd.Flags().GetString("ignore-file")
Expand Down Expand Up @@ -238,6 +239,7 @@ func GetLintCommand() *cobra.Command {
IgnoreArrayCircleRef: ignoreArrayCircleRef,
IgnorePolymorphCircleRef: ignorePolymorphCircleRef,
IgnoredResults: ignoredItems,
ExtensionRefs: extensionRefsFlag,
}
fs, fp, err := lintFile(lfr)

Expand Down Expand Up @@ -338,18 +340,19 @@ func lintFile(req utils.LintFileRequest) (int64, int, error) {
}

result := motor.ApplyRulesToRuleSet(&motor.RuleSetExecution{
RuleSet: req.SelectedRS,
Spec: specBytes,
SpecFileName: req.FileName,
CustomFunctions: req.Functions,
Base: req.BaseFlag,
AllowLookup: req.Remote,
SkipDocumentCheck: req.SkipCheckFlag,
Logger: req.Logger,
BuildDeepGraph: deepGraph,
Timeout: time.Duration(req.TimeoutFlag) * time.Second,
IgnoreCircularArrayRef: req.IgnoreArrayCircleRef,
IgnoreCircularPolymorphicRef: req.IgnorePolymorphCircleRef,
RuleSet: req.SelectedRS,
Spec: specBytes,
SpecFileName: req.FileName,
CustomFunctions: req.Functions,
Base: req.BaseFlag,
AllowLookup: req.Remote,
SkipDocumentCheck: req.SkipCheckFlag,
Logger: req.Logger,
BuildDeepGraph: deepGraph,
Timeout: time.Duration(req.TimeoutFlag) * time.Second,
IgnoreCircularArrayRef: req.IgnoreArrayCircleRef,
IgnoreCircularPolymorphicRef: req.IgnorePolymorphCircleRef,
ExtractReferencesFromExtensions: req.ExtensionRefs,
})

result.Results = filterIgnoredResults(result.Results, req.IgnoredResults)
Expand Down
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ func GetRootCommand() *cobra.Command {
rootCmd.PersistentFlags().BoolP("debug", "w", false, "Turn on debug logging")
rootCmd.PersistentFlags().IntP("timeout", "g", 5, "Rule timeout in seconds, default is 5 seconds")
rootCmd.PersistentFlags().BoolP("hard-mode", "z", false, "Enable all the built-in rules, even the OWASP ones. This is the level to beat!")
rootCmd.PersistentFlags().BoolP("ext-refs", "", false, "Turn on $ref lookups and resolving for extensions (x-) objects")

if regErr := rootCmd.RegisterFlagCompletionFunc("functions", cobra.FixedCompletions(
[]string{"so"}, cobra.ShellCompDirectiveFilterFileExt,
Expand Down
16 changes: 9 additions & 7 deletions cmd/spectral_report.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func GetSpectralReportCommand() *cobra.Command {
skipCheckFlag, _ := cmd.Flags().GetBool("skip-check")
timeoutFlag, _ := cmd.Flags().GetInt("timeout")
hardModeFlag, _ := cmd.Flags().GetBool("hard-mode")
extensionRefsFlag, _ := cmd.Flags().GetBool("ext-refs")

// disable color and styling, for CI/CD use.
// https://github.com/daveshanley/vacuum/issues/234
Expand Down Expand Up @@ -151,13 +152,14 @@ func GetSpectralReportCommand() *cobra.Command {
}

ruleset := motor.ApplyRulesToRuleSet(&motor.RuleSetExecution{
RuleSet: selectedRS,
Spec: specBytes,
CustomFunctions: customFunctions,
SilenceLogs: true,
Base: baseFlag,
SkipDocumentCheck: skipCheckFlag,
Timeout: time.Duration(timeoutFlag) * time.Second,
RuleSet: selectedRS,
Spec: specBytes,
CustomFunctions: customFunctions,
SilenceLogs: true,
Base: baseFlag,
SkipDocumentCheck: skipCheckFlag,
Timeout: time.Duration(timeoutFlag) * time.Second,
ExtractReferencesFromExtensions: extensionRefsFlag,
})

resultSet := model.NewRuleResultSet(ruleset.Results)
Expand Down
18 changes: 10 additions & 8 deletions cmd/vacuum_report.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ func GetVacuumReportCommand() *cobra.Command {
timeoutFlag, _ := cmd.Flags().GetInt("timeout")
hardModeFlag, _ := cmd.Flags().GetBool("hard-mode")
ignoreFile, _ := cmd.Flags().GetString("ignore-file")
extensionRefsFlag, _ := cmd.Flags().GetBool("ext-refs")

// disable color and styling, for CI/CD use.
// https://github.com/daveshanley/vacuum/issues/234
Expand Down Expand Up @@ -172,14 +173,15 @@ func GetVacuumReportCommand() *cobra.Command {
}

ruleset := motor.ApplyRulesToRuleSet(&motor.RuleSetExecution{
RuleSet: selectedRS,
Spec: specBytes,
CustomFunctions: customFunctions,
SilenceLogs: true,
Base: baseFlag,
SkipDocumentCheck: skipCheckFlag,
BuildDeepGraph: deepGraph,
Timeout: time.Duration(timeoutFlag) * time.Second,
RuleSet: selectedRS,
Spec: specBytes,
CustomFunctions: customFunctions,
SilenceLogs: true,
Base: baseFlag,
SkipDocumentCheck: skipCheckFlag,
BuildDeepGraph: deepGraph,
Timeout: time.Duration(timeoutFlag) * time.Second,
ExtractReferencesFromExtensions: extensionRefsFlag,
})

resultSet := model.NewRuleResultSet(ruleset.Results)
Expand Down
47 changes: 25 additions & 22 deletions motor/rule_applicator.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,26 +54,27 @@ type ruleContext struct {
// RuleSetExecution is an instruction set for executing a ruleset. It's a convenience structure to allow the signature
// of ApplyRulesToRuleSet to change, without a huge refactor. The ApplyRulesToRuleSet function only returns a single error also.
type RuleSetExecution struct {
RuleSet *rulesets.RuleSet // The RuleSet in which to apply
SpecFileName string // The path of the specification file, used to correctly label location
Spec []byte // The raw bytes of the OpenAPI specification.
SpecInfo *datamodel.SpecInfo // Pre-parsed spec-info.
IndexUnresolved *index.SpecIndex // The unresolved index, even if a file is not an OpenAPI spec, it's still indexed.
IndexResolved *index.SpecIndex // The resolved index, like the unresolved one, but with references resolved.
CustomFunctions map[string]model.RuleFunction // custom functions loaded from plugin.
PanicFunction func(p any) // In case of emergency, do this thing here.
SilenceLogs bool // Prevent any warnings about rules/rule-sets being printed.
Base string // The base path or URL of the specification, used for resolving relative or remote paths.
AllowLookup bool // Allow remote lookup of files or links
Document libopenapi.Document // a ready to render model.
DrDocument *doctorModel.DrDocument // a high level, more powerful model, powered by the doctorModel.
SkipDocumentCheck bool // Skip the document check, useful for fragments and non openapi specs.
Logger *slog.Logger // A custom logger.
Timeout time.Duration // The timeout for each rule to run, prevents run-away rules, default is five seconds.
NodeLookupTimeout time.Duration // The timeout for each node yaml path lookup, prevents any endless loops, default is 500ms (https://github.com/daveshanley/vacuum/issues/502)
BuildGraph bool // Build a graph of the document, powered by the doctorModel. (default is false)
BuildDeepGraph bool // Build a deep graph of the document, all paths in the graph will be followed, no caching on schemas. (default is false). Required when using ignore files as an object can be referenced in multiple places.
ExtractReferencesSequentially bool // Extract references sequentially, defaults to false, can be slow.
RuleSet *rulesets.RuleSet // The RuleSet in which to apply
SpecFileName string // The path of the specification file, used to correctly label location
Spec []byte // The raw bytes of the OpenAPI specification.
SpecInfo *datamodel.SpecInfo // Pre-parsed spec-info.
IndexUnresolved *index.SpecIndex // The unresolved index, even if a file is not an OpenAPI spec, it's still indexed.
IndexResolved *index.SpecIndex // The resolved index, like the unresolved one, but with references resolved.
CustomFunctions map[string]model.RuleFunction // custom functions loaded from plugin.
PanicFunction func(p any) // In case of emergency, do this thing here.
SilenceLogs bool // Prevent any warnings about rules/rule-sets being printed.
Base string // The base path or URL of the specification, used for resolving relative or remote paths.
AllowLookup bool // Allow remote lookup of files or links
Document libopenapi.Document // a ready to render model.
DrDocument *doctorModel.DrDocument // a high level, more powerful model, powered by the doctorModel.
SkipDocumentCheck bool // Skip the document check, useful for fragments and non openapi specs.
Logger *slog.Logger // A custom logger.
Timeout time.Duration // The timeout for each rule to run, prevents run-away rules, default is five seconds.
NodeLookupTimeout time.Duration // The timeout for each node yaml path lookup, prevents any endless loops, default is 500ms (https://github.com/daveshanley/vacuum/issues/502)
BuildGraph bool // Build a graph of the document, powered by the doctorModel. (default is false)
BuildDeepGraph bool // Build a deep graph of the document, all paths in the graph will be followed, no caching on schemas. (default is false). Required when using ignore files as an object can be referenced in multiple places.
ExtractReferencesSequentially bool // Extract references sequentially, defaults to false, can be slow.
ExtractReferencesFromExtensions bool // Extract references from extension objects (x-), this may pull in all kinds of non-parsable files in.

// https://pb33f.io/libopenapi/circular-references/#circular-reference-results
IgnoreCircularArrayRef bool // Ignore array circular references
Expand Down Expand Up @@ -118,17 +119,16 @@ func ApplyRulesToRuleSet(execution *RuleSetExecution) *RuleSetExecutionResult {

// create new configurations
indexConfig := index.CreateClosedAPIIndexConfig()
indexConfig.ExcludeExtensionRefs = true // disable references in extensions being extracted by default
indexConfig.SpecFilePath = execution.SpecFileName
indexConfigUnresolved := index.CreateClosedAPIIndexConfig()
indexConfigUnresolved.SpecFilePath = execution.SpecFileName

// avoid building the index, we don't need it to run yet.
indexConfig.AvoidBuildIndex = true
//indexConfig.AvoidCircularReferenceCheck = true

docConfig := datamodel.NewDocumentConfiguration()
docConfig.SpecFilePath = execution.SpecFileName
//docConfig.SkipCircularReferenceCheck = true

if execution.IgnoreCircularArrayRef {
docConfig.IgnoreArrayCircularReferences = true
Expand All @@ -137,6 +137,9 @@ func ApplyRulesToRuleSet(execution *RuleSetExecution) *RuleSetExecutionResult {
if execution.IgnoreCircularPolymorphicRef {
docConfig.IgnorePolymorphicCircularReferences = true
}
if execution.ExtractReferencesFromExtensions {
indexConfig.ExcludeExtensionRefs = false
}

// add new pretty logger.
if execution.Logger == nil {
Expand Down
1 change: 1 addition & 0 deletions utils/lint_file_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type LintFileRequest struct {
DetailsFlag bool
TimeFlag bool
NoMessageFlag bool
ExtensionRefs bool
AllResultsFlag bool
FailSeverityFlag string
CategoryFlag string
Expand Down

0 comments on commit 9789cf5

Please sign in to comment.