Merge "b/163140302 Add schema validation for x-security-fresh extension"
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtendedValidator.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtendedValidator.java
index 32a24a9..18400b8 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtendedValidator.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtendedValidator.java
@@ -1,11 +1,14 @@
 package com.apigee.security.oas.extendedvalidator;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.flogger.FluentLogger;
 import javax.inject.Inject;
 import org.openapi4j.parser.model.v3.OpenApi3;
 
 final class BaseExtendedValidator implements ExtendedValidator {
 
+  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
   private final TraversalHelperFactory traversalHelperFactory;
 
   @Inject
@@ -17,5 +20,9 @@
   public void validate(OpenApi3 openApiSpec) {
     TraversalHelper traversalHelper = traversalHelperFactory.create(ImmutableList.of());
     traversalHelper.sendOpenApiTraversal(openApiSpec);
+    ImmutableSet<Extension> extensions = traversalHelper.traverse();
+
+    // TODO(b/161441872) : Add extension validation logic
+    logger.atInfo().log("%s", extensions);
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtensionSchemaValidator.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtensionSchemaValidator.java
index b576fb9..c6ad87c 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtensionSchemaValidator.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtensionSchemaValidator.java
@@ -1,5 +1,6 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import static com.apigee.security.oas.extendedvalidator.ErrorMessage.INVALID_SCHEMA;
 import static com.apigee.security.oas.extendedvalidator.ExtensionName.valueOfExtensionName;
 import static com.apigee.security.oas.extendedvalidator.ExtensionValidators.defaultErrors;
 import static com.apigee.security.oas.extendedvalidator.ExtensionValidators.prepareNamedPath;
@@ -83,7 +84,8 @@
       JsonSchema schema = getJsonSchemaFromUrl(schemaUrl);
       Set<ValidationMessage> errors = schema.validate(extension.getExtensionContent());
 
-      return toExtensionValidationMessages(errors, "INVALID_SCHEMA", prepareNamedPath(extension));
+      return toExtensionValidationMessages(
+          errors, INVALID_SCHEMA.getErrorType(), prepareNamedPath(extension));
 
     } catch (URISyntaxException e) {
       String errorMessage = "Failed to validate extension schema";
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtensionScopeValidator.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtensionScopeValidator.java
new file mode 100644
index 0000000..8ff013a
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtensionScopeValidator.java
@@ -0,0 +1,94 @@
+package com.apigee.security.oas.extendedvalidator;
+
+import static com.apigee.security.oas.extendedvalidator.ErrorMessage.X_SECURITY_ALLOW_SCOPE_ERROR;
+import static com.apigee.security.oas.extendedvalidator.ErrorMessage.X_SECURITY_TYPE_DEFINITIONS_SCOPE_ERROR;
+import static com.apigee.security.oas.extendedvalidator.ErrorMessage.X_SECURITY_TYPE_SCOPE_ERROR;
+import static com.apigee.security.oas.extendedvalidator.ExtensionName.X_SECURITY_ALLOW;
+import static com.apigee.security.oas.extendedvalidator.ExtensionName.X_SECURITY_TYPE;
+import static com.apigee.security.oas.extendedvalidator.ExtensionName.X_SECURITY_TYPE_DEFINITIONS;
+import static com.apigee.security.oas.extendedvalidator.ExtensionName.valueOfExtensionName;
+import static com.apigee.security.oas.extendedvalidator.ExtensionValidators.defaultErrors;
+import static com.apigee.security.oas.extendedvalidator.ExtensionValidators.fetchExtensionParent;
+import static com.apigee.security.oas.extendedvalidator.ExtensionValidators.prepareNamedPath;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.flogger.FluentLogger;
+import java.util.Optional;
+import org.openapi4j.parser.model.OpenApiSchema;
+import org.openapi4j.parser.model.v3.OpenApi3;
+import org.openapi4j.parser.model.v3.Operation;
+import org.openapi4j.parser.model.v3.Parameter;
+import org.openapi4j.parser.model.v3.Schema;
+
+final class BaseExtensionScopeValidator implements ExtensionScopeValidator {
+
+  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+  private static final ImmutableMap<ExtensionName, ErrorMessage> errorMessages =
+      Maps.immutableEnumMap(
+          ImmutableMap.of(
+              X_SECURITY_TYPE, X_SECURITY_TYPE_SCOPE_ERROR,
+              X_SECURITY_ALLOW, X_SECURITY_ALLOW_SCOPE_ERROR,
+              X_SECURITY_TYPE_DEFINITIONS, X_SECURITY_TYPE_DEFINITIONS_SCOPE_ERROR));
+
+  private static final ImmutableMap<ExtensionName, ImmutableSet<Class<? extends OpenApiSchema>>>
+      allowedParentsMap =
+          Maps.immutableEnumMap(
+              ImmutableMap.of(
+                  X_SECURITY_TYPE, ImmutableSet.of(Schema.class, Parameter.class),
+                  X_SECURITY_ALLOW, ImmutableSet.of(Operation.class),
+                  X_SECURITY_TYPE_DEFINITIONS, ImmutableSet.of(OpenApi3.class)));
+
+  /**
+   * Validates the scope of an supported {@link Extension} by contrasting it against its appropriate
+   * scope.
+   *
+   * <p>An invalid scoped Extension will return {@code INVALID_SCOPE} {@link
+   * ExtensionValidationMessage}.
+   */
+  @Override
+  public ImmutableSet<ExtensionValidationMessage> validate(Extension extension) {
+    Optional<ExtensionName> extensionNameOptional =
+        valueOfExtensionName(extension.getExtensionName());
+    ImmutableSet.Builder<ExtensionValidationMessage> errors = ImmutableSet.builder();
+
+    extensionNameOptional.ifPresentOrElse(
+        extensionNameEnum -> errors.addAll(validateExtensionScope(extension, extensionNameEnum)),
+        () -> errors.addAll(defaultErrors(extension)));
+
+    logger.atInfo().log("Scope Errors(%s) : %s", prepareNamedPath(extension), errors);
+
+    return errors.build();
+  }
+
+  /**
+   * Checks whether {@link Extension} exists under an allowed parent class.
+   *
+   * <p>A parent class is allowed if {@link Extension} has an entry in {@link
+   * BaseExtensionScopeValidator#allowedParentsMap} with it.
+   */
+  private static ImmutableSet<ExtensionValidationMessage> validateExtensionScope(
+      Extension extension, ExtensionName extensionNameEnum) {
+
+    Optional<Class<? extends OpenApiSchema>> extensionParent = fetchExtensionParent(extension);
+
+    if (extensionParent.isPresent()
+        && allowedParentsMap.get(extensionNameEnum).contains(extensionParent.get())) {
+      return ImmutableSet.of();
+    }
+
+    return ImmutableSet.of(prepareInvalidScopeValidationMessage(extension, extensionNameEnum));
+  }
+
+  private static ExtensionValidationMessage prepareInvalidScopeValidationMessage(
+      Extension extension, ExtensionName extensionNameEnum) {
+
+    ErrorMessage errorMessageEnum = errorMessages.get(extensionNameEnum);
+    return ExtensionValidationMessage.builder()
+        .setType(errorMessageEnum.getErrorType())
+        .setMessage(errorMessageEnum.getErrorMessage())
+        .setPath(prepareNamedPath(extension))
+        .build();
+  }
+}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtensionValidationIntegrator.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtensionValidationIntegrator.java
index 055b696..1fc861e 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtensionValidationIntegrator.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtensionValidationIntegrator.java
@@ -1,13 +1,20 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+import java.util.Optional;
+import org.openapi4j.parser.model.OpenApiSchema;
 
 final class BaseExtensionValidationIntegrator extends ExtensionValidationIntegrator {
 
   // TODO(b/161441872): Inject ExtensionValidationIntegrator member dependencies.
 
   @Override
-  public void validateExtensions(ImmutableMap<String, Object> extensions) {
+  public void validateExtensions(
+      ImmutableMap<String, Object> extensions,
+      ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>>
+          updatedTraversalPath) {
     // TODO(b/161441872): Add validation logic here.
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseTraversalCoordinator.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseTraversalCoordinator.java
index b08fc02..b2b2769 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseTraversalCoordinator.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseTraversalCoordinator.java
@@ -1,6 +1,8 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import com.google.common.collect.ImmutableSet;
 import java.util.ArrayDeque;
+import java.util.Optional;
 
 /** Executes {@linkplain Traversal TraversalCommands} in a depth first (right to left) fashion. */
 final class BaseTraversalCoordinator implements TraversalCoordinator {
@@ -8,10 +10,15 @@
   private final ArrayDeque<TraversalCommand> stack = new ArrayDeque<>();
 
   @Override
-  public void traverse() {
+  public ImmutableSet<Extension> traverse() {
+    ImmutableSet.Builder<Extension> setBuilder = new ImmutableSet.Builder<>();
+
     while (!stack.isEmpty()) {
-      stack.pop().traverse();
+      Optional.ofNullable(stack.pop().traverse())
+          .ifPresent(extensions -> setBuilder.addAll(extensions));
     }
+
+    return setBuilder.build();
   }
 
   @Override
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseTraversalHelper.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseTraversalHelper.java
index 58e4e14..f160c01 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseTraversalHelper.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseTraversalHelper.java
@@ -2,6 +2,7 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import com.google.inject.assistedinject.Assisted;
 import java.util.Map;
 import java.util.Optional;
@@ -47,6 +48,11 @@
   }
 
   @Override
+  public ImmutableSet<Extension> traverse() {
+    return coordinator.traverse();
+  }
+
+  @Override
   public void sendCallbackTraversals(ImmutableMap<String, Callback> callbackMap) {
     Optional.ofNullable(callbackMap)
         .ifPresent(
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/CallbackTraversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/CallbackTraversal.java
index 7ed41fc..c00db58 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/CallbackTraversal.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/CallbackTraversal.java
@@ -1,6 +1,9 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import static com.apigee.security.oas.extendedvalidator.Immutables.toImmutableMap;
+
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.inject.assistedinject.Assisted;
 import java.util.Map;
 import java.util.Optional;
@@ -13,26 +16,25 @@
   @Inject
   CallbackTraversal(
       TraversalHelperFactory traversalHelperFactory,
-      ExtensionValidationIntegrator extensionValidationIntegrator,
+      ExtensionFactory extensionFactory,
       @Assisted
           ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath,
       @Assisted Callback openApiSchemaObj,
       @Assisted String name) {
-    super(
-        traversalHelperFactory,
-        extensionValidationIntegrator,
-        traversalPath,
-        openApiSchemaObj,
-        name);
+    super(traversalHelperFactory, extensionFactory, traversalPath, openApiSchemaObj, name);
   }
 
   @Override
-  public void traverse() {
-    // TODO(b/161441872): Process extensions.
+  public ImmutableSet<Extension> traverse() {
+    ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>>
+        updatedTraversalPath = getUpdatedTraversalPath();
 
-    TraversalHelper traversalHelper = traversalHelperFactory.create(getUpdatedTraversalPath());
+    TraversalHelper traversalHelper = traversalHelperFactory.create(updatedTraversalPath);
 
     traversalHelper.sendPathTraversals(
         Immutables.toImmutableMap(openApiSchemaObj.getCallbackPaths()));
+
+    return generateExtensions(
+        toImmutableMap(openApiSchemaObj.getExtensions()), updatedTraversalPath);
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ContactTraversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ContactTraversal.java
index 9c6691f..3c07fb6 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ContactTraversal.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ContactTraversal.java
@@ -1,6 +1,9 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import static com.apigee.security.oas.extendedvalidator.Immutables.toImmutableMap;
+
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.inject.assistedinject.Assisted;
 import java.util.Map;
 import java.util.Optional;
@@ -14,15 +17,16 @@
   @Inject
   ContactTraversal(
       TraversalHelperFactory traversalHelperFactory,
-      ExtensionValidationIntegrator extensionValidationIntegrator,
+      ExtensionFactory extensionFactory,
       @Assisted
           ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath,
       @Assisted Contact openApiSchemaObj) {
-    super(traversalHelperFactory, extensionValidationIntegrator, traversalPath, openApiSchemaObj);
+    super(traversalHelperFactory, extensionFactory, traversalPath, openApiSchemaObj);
   }
 
   @Override
-  public void traverse() {
-    // TODO(b/161441872): Process extensions.
+  public ImmutableSet<Extension> traverse() {
+    return generateExtensions(
+        toImmutableMap(openApiSchemaObj.getExtensions()), getUpdatedTraversalPath());
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ErrorMessage.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ErrorMessage.java
new file mode 100644
index 0000000..40b6c53
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ErrorMessage.java
@@ -0,0 +1,30 @@
+package com.apigee.security.oas.extendedvalidator;
+
+/** Stores information of {@link Extension} validation errors for ease of use. */
+enum ErrorMessage {
+  UNSUPPORTED_EXTENSION("UNSUPPORTED_EXTENSION", "extension is not binded to a validator."),
+  X_SECURITY_TYPE_SCOPE_ERROR(
+      "INVALID_SCOPE", "extension should be under a schema or parameter object."),
+  X_SECURITY_ALLOW_SCOPE_ERROR(
+      "INVALID_SCOPE",
+      "extension should be under a paths operation object like get, post, patch, etc."),
+  X_SECURITY_TYPE_DEFINITIONS_SCOPE_ERROR(
+      "INVALID_SCOPE", "extension should be on the top-level scope."),
+  INVALID_SCHEMA("INVALID_SCHEMA", "extension has an invalid schema.");
+
+  private final String errorType;
+  private final String errorMessage;
+
+  ErrorMessage(String errorType, String errorMessage) {
+    this.errorType = errorType;
+    this.errorMessage = errorMessage;
+  }
+
+  String getErrorType() {
+    return errorType;
+  }
+
+  String getErrorMessage() {
+    return errorMessage;
+  }
+}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtendedValidatorModule.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtendedValidatorModule.java
index f7b0dc0..8237d03 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtendedValidatorModule.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtendedValidatorModule.java
@@ -2,6 +2,7 @@
 
 import com.google.inject.AbstractModule;
 import com.google.inject.assistedinject.FactoryModuleBuilder;
+import javax.inject.Singleton;
 
 /**
  * Configures the bindings of inner components to be installed in the {@link
@@ -15,7 +16,7 @@
 
     bind(ExtensionValidationIntegrator.class).to(BaseExtensionValidationIntegrator.class);
 
-    bind(TraversalCoordinator.class).to(BaseTraversalCoordinator.class);
+    bind(TraversalCoordinator.class).to(BaseTraversalCoordinator.class).in(Singleton.class);
 
     install(
         new FactoryModuleBuilder()
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionModule.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionModule.java
index 70c3f7a..bd083e2 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionModule.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionModule.java
@@ -19,6 +19,7 @@
             .build(ExtensionFactory.class));
 
     bind(ExtensionSchemaValidator.class).to(BaseExtensionSchemaValidator.class);
+    bind(ExtensionScopeValidator.class).to(BaseExtensionScopeValidator.class);
   }
 
   @Provides
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionScopeValidator.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionScopeValidator.java
new file mode 100644
index 0000000..bb0e888
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionScopeValidator.java
@@ -0,0 +1,9 @@
+package com.apigee.security.oas.extendedvalidator;
+
+/**
+ * Validates that {@link Extension} exists in a valid scope.
+ *
+ * <p>A valid scope of {@link Extension} is a defined {@link Class}<? extends {@link
+ * org.openapi4j.parser.model.OpenApiSchema}> object that it is allowed to exist under.
+ */
+interface ExtensionScopeValidator extends ExtensionValidator {}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionValidationIntegrator.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionValidationIntegrator.java
index 26fcaed..c38c062 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionValidationIntegrator.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionValidationIntegrator.java
@@ -1,6 +1,10 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+import java.util.Optional;
+import org.openapi4j.parser.model.OpenApiSchema;
 
 /** Carries out the process of validating ApiSecurityTool's OpenApi extensions. */
 public abstract class ExtensionValidationIntegrator {
@@ -11,5 +15,8 @@
    * Validates extensions from a mapping of extension {@linkplain String name} to extension
    * {@linkplain Object content}.
    */
-  abstract void validateExtensions(ImmutableMap<String, Object> extensions);
+  abstract void validateExtensions(
+      ImmutableMap<String, Object> extensions,
+      ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>>
+          updatedTraversalPath);
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionValidators.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionValidators.java
index d380ca7..1d85f31 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionValidators.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionValidators.java
@@ -1,5 +1,7 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import static com.apigee.security.oas.extendedvalidator.ErrorMessage.UNSUPPORTED_EXTENSION;
+
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.networknt.schema.ValidationMessage;
@@ -72,11 +74,15 @@
     return ImmutableSet.copyOf(updatedErrors);
   }
 
-  // TODO(b/162938113) : Create validation error type and message constants.
   /** Prepares {@link ExtensionValidationMessage} for extensions without validators. */
   static ImmutableSet<ExtensionValidationMessage> defaultErrors(Extension extension) {
-    String errorType = "UNSUPPORTED_EXTENSION";
-    String errorMessage = extension.getExtensionName() + " is not binded to the validator.";
+    String errorType = UNSUPPORTED_EXTENSION.getErrorType();
+    String errorMessage =
+        new StringBuilder()
+            .append(extension.getExtensionName())
+            .append(" ")
+            .append(UNSUPPORTED_EXTENSION.getErrorMessage())
+            .toString();
     String path = prepareNamedPath(extension);
     return ImmutableSet.of(
         ExtensionValidationMessage.builder()
@@ -86,5 +92,16 @@
             .build());
   }
 
+  static Optional<Class<? extends OpenApiSchema>> fetchExtensionParent(Extension extension) {
+    ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> extensionPath =
+        extension.getExtensionPath();
+
+    if (extensionPath.isEmpty()) {
+      return Optional.empty();
+    }
+
+    return Optional.of(extensionPath.get(extensionPath.size() - 1).getKey());
+  }
+
   private ExtensionValidators() {}
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExternalDocsTraversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExternalDocsTraversal.java
index 47b08ab..072ac6d 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExternalDocsTraversal.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExternalDocsTraversal.java
@@ -1,6 +1,9 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import static com.apigee.security.oas.extendedvalidator.Immutables.toImmutableMap;
+
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.inject.assistedinject.Assisted;
 import java.util.Map;
 import java.util.Optional;
@@ -15,16 +18,17 @@
   @Inject
   ExternalDocsTraversal(
       TraversalHelperFactory traversalHelperFactory,
-      ExtensionValidationIntegrator extensionValidationIntegrator,
+      ExtensionFactory extensionFactory,
       @Assisted ExternalDocs openApiSchemaObj,
       @Assisted
           ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>>
               traversalPath) {
-    super(traversalHelperFactory, extensionValidationIntegrator, traversalPath, openApiSchemaObj);
+    super(traversalHelperFactory, extensionFactory, traversalPath, openApiSchemaObj);
   }
 
   @Override
-  public void traverse() {
-    // TODO(b/161441872): Process extensions.
+  public ImmutableSet<Extension> traverse() {
+    return generateExtensions(
+        toImmutableMap(openApiSchemaObj.getExtensions()), getUpdatedTraversalPath());
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/InfoTraversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/InfoTraversal.java
index 5d89df2..d1df744 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/InfoTraversal.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/InfoTraversal.java
@@ -1,6 +1,9 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import static com.apigee.security.oas.extendedvalidator.Immutables.toImmutableMap;
+
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.inject.assistedinject.Assisted;
 import java.util.Map;
 import java.util.Optional;
@@ -14,20 +17,24 @@
   @Inject
   InfoTraversal(
       TraversalHelperFactory traversalHelperFactory,
-      ExtensionValidationIntegrator extensionValidationIntegrator,
+      ExtensionFactory extensionFactory,
       @Assisted
           ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath,
       @Assisted Info openApiSchemaObj) {
-    super(traversalHelperFactory, extensionValidationIntegrator, traversalPath, openApiSchemaObj);
+    super(traversalHelperFactory, extensionFactory, traversalPath, openApiSchemaObj);
   }
 
   @Override
-  public void traverse() {
-    // TODO(b/161441872): Process extensions.
+  public ImmutableSet<Extension> traverse() {
+    ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>>
+        updatedTraversalPath = getUpdatedTraversalPath();
 
-    TraversalHelper traversalHelper = traversalHelperFactory.create(getUpdatedTraversalPath());
+    TraversalHelper traversalHelper = traversalHelperFactory.create(updatedTraversalPath);
 
     traversalHelper.sendContactTraversal(openApiSchemaObj.getContact());
     traversalHelper.sendLicenseTraversal(openApiSchemaObj.getLicense());
+
+    return generateExtensions(
+        toImmutableMap(openApiSchemaObj.getExtensions()), updatedTraversalPath);
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/LicenseTraversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/LicenseTraversal.java
index e3b20f0..2e6116f 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/LicenseTraversal.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/LicenseTraversal.java
@@ -1,6 +1,9 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import static com.apigee.security.oas.extendedvalidator.Immutables.toImmutableMap;
+
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.inject.assistedinject.Assisted;
 import java.util.Map;
 import java.util.Optional;
@@ -14,15 +17,16 @@
   @Inject
   LicenseTraversal(
       TraversalHelperFactory traversalHelperFactory,
-      ExtensionValidationIntegrator extensionValidationIntegrator,
+      ExtensionFactory extensionFactory,
       @Assisted
           ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath,
       @Assisted License openApiSchemaObj) {
-    super(traversalHelperFactory, extensionValidationIntegrator, traversalPath, openApiSchemaObj);
+    super(traversalHelperFactory, extensionFactory, traversalPath, openApiSchemaObj);
   }
 
   @Override
-  public void traverse() {
-    // TODO(b/161441872): Process extensions.
+  public ImmutableSet<Extension> traverse() {
+    return generateExtensions(
+        toImmutableMap(openApiSchemaObj.getExtensions()), getUpdatedTraversalPath());
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversal.java
index 07b547f..c2ff58f 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversal.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversal.java
@@ -4,8 +4,12 @@
 import static com.apigee.security.oas.extendedvalidator.Immutables.toImmutableMap;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.inject.assistedinject.Assisted;
+import java.util.Map;
+import java.util.Optional;
 import javax.inject.Inject;
+import org.openapi4j.parser.model.OpenApiSchema;
 import org.openapi4j.parser.model.v3.OpenApi3;
 
 /** A {@link Traversal} that traverses an {@link OpenApi3} object. */
@@ -15,23 +19,28 @@
   @Inject
   OpenApiSpecificationTraversal(
       TraversalHelperFactory factory,
-      ExtensionValidationIntegrator extensionValidationIntegrator,
+      ExtensionFactory extensionFactory,
       @Assisted OpenApi3 openApiSchemaObj) {
-    super(factory, extensionValidationIntegrator, ImmutableList.of(), openApiSchemaObj);
+    super(factory, extensionFactory, ImmutableList.of(), openApiSchemaObj);
   }
 
   @Override
-  public void traverse() {
-    // TODO(b/161441872): Process extensions.
+  public ImmutableSet<Extension> traverse() {
+    ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>>
+        updatedTraversalPath = getUpdatedTraversalPath();
 
-    TraversalHelper traversalHelper = traversalHelperFactory.create(getUpdatedTraversalPath());
+    TraversalHelper traversalHelper = traversalHelperFactory.create(updatedTraversalPath);
 
     traversalHelper.sendExternalDocsTraversal(openApiSchemaObj.getExternalDocs());
     traversalHelper.sendInfoTraversal(openApiSchemaObj.getInfo());
     traversalHelper.sendServerTraversals(toImmutableList(openApiSchemaObj.getServers()));
     traversalHelper.sendTagTraversals(toImmutableList(openApiSchemaObj.getTags()));
     traversalHelper.sendPathTraversals(toImmutableMap(openApiSchemaObj.getPaths()));
-    // TODO(b/162682119): Send OpenApiSpecification SecurityRequirements to TraversalHelper.
+    traversalHelper.sendSecurityRequirementTraversals(
+        toImmutableList(openApiSchemaObj.getSecurityRequirements()));
     // TODO(b/162677159): Send Components traversal.
+
+    return generateExtensions(
+        toImmutableMap(openApiSchemaObj.getExtensions()), updatedTraversalPath);
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/OperationTraversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/OperationTraversal.java
index 22ed41d..4d89636 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/OperationTraversal.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/OperationTraversal.java
@@ -1,6 +1,9 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import static com.apigee.security.oas.extendedvalidator.Immutables.toImmutableMap;
+
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.inject.assistedinject.Assisted;
 import java.util.Map;
 import java.util.Optional;
@@ -14,24 +17,20 @@
   @Inject
   OperationTraversal(
       TraversalHelperFactory traversalHelperFactory,
-      ExtensionValidationIntegrator extensionValidationIntegrator,
+      ExtensionFactory extensionFactory,
       @Assisted
           ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath,
       @Assisted Operation openApiSchemaObj,
       @Assisted String name) {
-    super(
-        traversalHelperFactory,
-        extensionValidationIntegrator,
-        traversalPath,
-        openApiSchemaObj,
-        name);
+    super(traversalHelperFactory, extensionFactory, traversalPath, openApiSchemaObj, name);
   }
 
   @Override
-  public void traverse() {
-    // TODO(b/161441872): Process extensions.
+  public ImmutableSet<Extension> traverse() {
+    ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>>
+        updatedTraversalPath = getUpdatedTraversalPath();
 
-    TraversalHelper traversalHelper = traversalHelperFactory.create(getUpdatedTraversalPath());
+    TraversalHelper traversalHelper = traversalHelperFactory.create(updatedTraversalPath);
 
     traversalHelper.sendExternalDocsTraversal(openApiSchemaObj.getExternalDocs());
     traversalHelper.sendCallbackTraversals(
@@ -43,5 +42,8 @@
         Immutables.toImmutableMap(openApiSchemaObj.getResponses()));
     traversalHelper.sendSecurityRequirementTraversals(
         Immutables.toImmutableList(openApiSchemaObj.getSecurityRequirements()));
+
+    return generateExtensions(
+        toImmutableMap(openApiSchemaObj.getExtensions()), updatedTraversalPath);
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ParameterTraversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ParameterTraversal.java
index 869fcb7..02f9f28 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ParameterTraversal.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ParameterTraversal.java
@@ -1,6 +1,9 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import static com.apigee.security.oas.extendedvalidator.Immutables.toImmutableMap;
+
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.inject.assistedinject.Assisted;
 import java.util.Map;
 import java.util.Optional;
@@ -13,18 +16,20 @@
   @Inject
   ParameterTraversal(
       TraversalHelperFactory traversalHelperFactory,
-      ExtensionValidationIntegrator extensionValidationIntegrator,
+      ExtensionFactory extensionFactory,
       @Assisted
           ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath,
       @Assisted Parameter openApiSchemaObj) {
-    super(traversalHelperFactory, extensionValidationIntegrator, traversalPath, openApiSchemaObj);
+    super(traversalHelperFactory, extensionFactory, traversalPath, openApiSchemaObj);
   }
 
   @Override
-  public void traverse() {
-    // TODO(b/161441872): Process extensions.
+  public ImmutableSet<Extension> traverse() {
 
     // TODO(b/162897637): Send MediaType Traversal
     // TODO(b/162942899): Send Example Traversal
+
+    return generateExtensions(
+        toImmutableMap(openApiSchemaObj.getExtensions()), getUpdatedTraversalPath());
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/PathTraversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/PathTraversal.java
index 5ba0797..e6da1ce 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/PathTraversal.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/PathTraversal.java
@@ -1,6 +1,9 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import static com.apigee.security.oas.extendedvalidator.Immutables.toImmutableMap;
+
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.inject.assistedinject.Assisted;
 import java.util.Map;
 import java.util.Optional;
@@ -14,29 +17,28 @@
   @Inject
   PathTraversal(
       TraversalHelperFactory traversalHelperFactory,
-      ExtensionValidationIntegrator extensionValidationIntegrator,
+      ExtensionFactory extensionFactory,
       @Assisted
           ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath,
       @Assisted Path openApiSchemaObj,
       @Assisted String name) {
-    super(
-        traversalHelperFactory,
-        extensionValidationIntegrator,
-        traversalPath,
-        openApiSchemaObj,
-        name);
+    super(traversalHelperFactory, extensionFactory, traversalPath, openApiSchemaObj, name);
   }
 
   @Override
-  public void traverse() {
-    // TODO(b/161441872): Process extensions.
+  public ImmutableSet<Extension> traverse() {
+    ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>>
+        updatedTraversalPath = getUpdatedTraversalPath();
 
-    TraversalHelper traversalHelper = traversalHelperFactory.create(getUpdatedTraversalPath());
+    TraversalHelper traversalHelper = traversalHelperFactory.create(updatedTraversalPath);
 
     traversalHelper.sendParameterTraversals(
         Immutables.toImmutableList(openApiSchemaObj.getParameters()));
     traversalHelper.sendServerTraversals(Immutables.toImmutableList(openApiSchemaObj.getServers()));
     traversalHelper.sendOperationTraversals(
         Immutables.toImmutableMap(openApiSchemaObj.getOperations()));
+
+    return generateExtensions(
+        toImmutableMap(openApiSchemaObj.getExtensions()), updatedTraversalPath);
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ResponseTraversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ResponseTraversal.java
index d214ada..17370b6 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ResponseTraversal.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ResponseTraversal.java
@@ -1,6 +1,9 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import static com.apigee.security.oas.extendedvalidator.Immutables.toImmutableMap;
+
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.inject.assistedinject.Assisted;
 import java.util.Map;
 import java.util.Optional;
@@ -14,23 +17,20 @@
   @Inject
   ResponseTraversal(
       TraversalHelperFactory traversalHelperFactory,
-      ExtensionValidationIntegrator extensionValidationIntegrator,
+      ExtensionFactory extensionFactory,
       @Assisted
           ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath,
       @Assisted Response openApiSchemaObj,
       @Assisted String name) {
-    super(
-        traversalHelperFactory,
-        extensionValidationIntegrator,
-        traversalPath,
-        openApiSchemaObj,
-        name);
+    super(traversalHelperFactory, extensionFactory, traversalPath, openApiSchemaObj, name);
   }
 
   @Override
-  public void traverse() {
+  public ImmutableSet<Extension> traverse() {
     // TODO(b/162897637): Send MediaType Traversals.
     // TODO(b/162949105): Send Header Traversals.
     // TODO(b/162949106): Send Link Traversals.
+    return generateExtensions(
+        toImmutableMap(openApiSchemaObj.getExtensions()), getUpdatedTraversalPath());
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/SecurityRequirementTraversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/SecurityRequirementTraversal.java
index 78f614e..ce8f0cd 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/SecurityRequirementTraversal.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/SecurityRequirementTraversal.java
@@ -1,6 +1,7 @@
 package com.apigee.security.oas.extendedvalidator;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.inject.assistedinject.Assisted;
 import java.util.Map;
 import java.util.Optional;
@@ -14,15 +15,16 @@
   @Inject
   SecurityRequirementTraversal(
       TraversalHelperFactory traversalHelperFactory,
-      ExtensionValidationIntegrator extensionValidationIntegrator,
+      ExtensionFactory extensionFactory,
       @Assisted
           ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath,
       @Assisted SecurityRequirement openApiSchemaObj) {
-    super(traversalHelperFactory, extensionValidationIntegrator, traversalPath, openApiSchemaObj);
+    super(traversalHelperFactory, extensionFactory, traversalPath, openApiSchemaObj);
   }
 
   @Override
-  public void traverse() {
-    // TODO(b/161441872): Process extensions.
+  public ImmutableSet<Extension> traverse() {
+    // SecurityRequirement object cannot have extensions.
+    return ImmutableSet.of();
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ServerTraversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ServerTraversal.java
index c464d7a..160ee27 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ServerTraversal.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ServerTraversal.java
@@ -3,6 +3,7 @@
 import static com.apigee.security.oas.extendedvalidator.Immutables.toImmutableMap;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.inject.assistedinject.Assisted;
 import java.util.Map;
 import java.util.Optional;
@@ -16,19 +17,23 @@
   @Inject
   ServerTraversal(
       TraversalHelperFactory traversalHelperFactory,
-      ExtensionValidationIntegrator extensionValidationIntegrator,
+      ExtensionFactory extensionFactory,
       @Assisted
           ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath,
       @Assisted Server openApiSchemaObj) {
-    super(traversalHelperFactory, extensionValidationIntegrator, traversalPath, openApiSchemaObj);
+    super(traversalHelperFactory, extensionFactory, traversalPath, openApiSchemaObj);
   }
 
   @Override
-  public void traverse() {
-    // TODO(b/161441872): Process extensions.
+  public ImmutableSet<Extension> traverse() {
+    ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>>
+        updatedTraversalPath = getUpdatedTraversalPath();
 
-    TraversalHelper traversalHelper = traversalHelperFactory.create(getUpdatedTraversalPath());
+    TraversalHelper traversalHelper = traversalHelperFactory.create(updatedTraversalPath);
 
     traversalHelper.sendServerVariableTraversals(toImmutableMap(openApiSchemaObj.getVariables()));
+
+    return generateExtensions(
+        toImmutableMap(openApiSchemaObj.getExtensions()), updatedTraversalPath);
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ServerVariableTraversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ServerVariableTraversal.java
index d1284ef..ddb7e27 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ServerVariableTraversal.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ServerVariableTraversal.java
@@ -1,6 +1,9 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import static com.apigee.security.oas.extendedvalidator.Immutables.toImmutableMap;
+
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.inject.assistedinject.Assisted;
 import java.util.Map;
 import java.util.Optional;
@@ -15,21 +18,17 @@
   @Inject
   ServerVariableTraversal(
       TraversalHelperFactory traversalHelperFactory,
-      ExtensionValidationIntegrator extensionValidationIntegrator,
+      ExtensionFactory extensionFactory,
       @Assisted
           ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath,
       @Assisted ServerVariable openApiSchemaObj,
       @Assisted String name) {
-    super(
-        traversalHelperFactory,
-        extensionValidationIntegrator,
-        traversalPath,
-        openApiSchemaObj,
-        name);
+    super(traversalHelperFactory, extensionFactory, traversalPath, openApiSchemaObj, name);
   }
 
   @Override
-  public void traverse() {
-    // TODO(b/161441872): Process extensions.
+  public ImmutableSet<Extension> traverse() {
+    return generateExtensions(
+        toImmutableMap(openApiSchemaObj.getExtensions()), getUpdatedTraversalPath());
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TagTraversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TagTraversal.java
index c6e9a40..5a21d2c 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TagTraversal.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TagTraversal.java
@@ -1,6 +1,9 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import static com.apigee.security.oas.extendedvalidator.Immutables.toImmutableMap;
+
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.inject.assistedinject.Assisted;
 import java.util.Map;
 import java.util.Optional;
@@ -14,19 +17,23 @@
   @Inject
   TagTraversal(
       TraversalHelperFactory traversalHelperFactory,
-      ExtensionValidationIntegrator extensionValidationIntegrator,
+      ExtensionFactory extensionFactory,
       @Assisted
           ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath,
       @Assisted Tag openApiSchemaObj) {
-    super(traversalHelperFactory, extensionValidationIntegrator, traversalPath, openApiSchemaObj);
+    super(traversalHelperFactory, extensionFactory, traversalPath, openApiSchemaObj);
   }
 
   @Override
-  public void traverse() {
-    // TODO(b/161441872): Process extensions.
+  public ImmutableSet<Extension> traverse() {
+    ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>>
+        updatedTraversalPath = getUpdatedTraversalPath();
 
-    TraversalHelper traversalHelper = traversalHelperFactory.create(getUpdatedTraversalPath());
+    TraversalHelper traversalHelper = traversalHelperFactory.create(updatedTraversalPath);
 
     traversalHelper.sendExternalDocsTraversal(openApiSchemaObj.getExternalDocs());
+
+    return generateExtensions(
+        toImmutableMap(openApiSchemaObj.getExtensions()), updatedTraversalPath);
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/Traversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/Traversal.java
index 37ebd51..0c3986f 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/Traversal.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/Traversal.java
@@ -1,6 +1,9 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import java.util.AbstractMap;
 import java.util.ArrayDeque;
 import java.util.Map;
@@ -11,19 +14,20 @@
 abstract class Traversal<T extends OpenApiSchema> {
 
   protected final T openApiSchemaObj;
-  protected final ExtensionValidationIntegrator extensionValidationIntegrator;
+  protected final ExtensionFactory extensionFactory;
   protected final TraversalHelperFactory traversalHelperFactory;
   private final ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>>
       traversalPath;
   private final Optional<String> name;
+  private final ObjectMapper objectMapper = new ObjectMapper();
 
   Traversal(
       TraversalHelperFactory traversalHelperFactory,
-      ExtensionValidationIntegrator extensionValidationIntegrator,
+      ExtensionFactory extensionFactory,
       ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath,
       T openApiSchemaObj) {
     this.traversalHelperFactory = traversalHelperFactory;
-    this.extensionValidationIntegrator = extensionValidationIntegrator;
+    this.extensionFactory = extensionFactory;
     this.traversalPath = traversalPath;
     this.openApiSchemaObj = openApiSchemaObj;
     this.name = Optional.empty();
@@ -31,12 +35,12 @@
 
   Traversal(
       TraversalHelperFactory traversalHelperFactory,
-      ExtensionValidationIntegrator extensionValidationIntegrator,
+      ExtensionFactory extensionFactory,
       ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath,
       T openApiSchemaObj,
       String name) {
     this.traversalHelperFactory = traversalHelperFactory;
-    this.extensionValidationIntegrator = extensionValidationIntegrator;
+    this.extensionFactory = extensionFactory;
     this.traversalPath = traversalPath;
     this.openApiSchemaObj = openApiSchemaObj;
     this.name = Optional.of(name);
@@ -50,4 +54,20 @@
         new AbstractMap.SimpleImmutableEntry<>(openApiSchemaObj.getClass(), name));
     return ImmutableList.copyOf(updatedTraversalPath);
   }
+
+  protected ImmutableSet<Extension> generateExtensions(
+      ImmutableMap<String, Object> extensions,
+      ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath) {
+    ImmutableSet.Builder<Extension> setBuilder = new ImmutableSet.Builder<>();
+
+    Optional.ofNullable(extensions)
+        .orElse(ImmutableMap.of())
+        .forEach(
+            (name, content) ->
+                setBuilder.add(
+                    extensionFactory.create(
+                        name, objectMapper.valueToTree(content), traversalPath)));
+
+    return setBuilder.build();
+  }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalCommand.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalCommand.java
index 257eb80..a2f5d9a 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalCommand.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalCommand.java
@@ -1,8 +1,10 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import com.google.common.collect.ImmutableSet;
+
 /** Used in conjunction with {@link Traversal} to traverse an OpenApi4j object. */
 public interface TraversalCommand {
 
-  /** Starts traversal logic. */
-  void traverse();
+  /** Starts traversal logic and collects {@link Extension extensions}. */
+  ImmutableSet<Extension> traverse();
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalCoordinator.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalCoordinator.java
index bb15592..0aa8568 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalCoordinator.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalCoordinator.java
@@ -1,13 +1,18 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import com.google.common.collect.ImmutableSet;
+
 /**
  * A {@link TraversalCoordinator} serves as the coordinator for otherwise interdependent {@linkplain
  * TraversalCommand TraversalCommands}.
  */
 public interface TraversalCoordinator {
 
-  /** Traverses handled {@linkplain Traversal TraversalCommands}. */
-  void traverse();
+  /**
+   * Traverses handled {@linkplain Traversal TraversalCommands} and returns {@link Extension
+   * extensions} found.
+   */
+  ImmutableSet<Extension> traverse();
 
   /**
    * Handles given {@link Traversal} in accordance with {@linkplain TraversalCoordinator
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalHelper.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalHelper.java
index 1f6ea32..680b896 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalHelper.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalHelper.java
@@ -2,6 +2,7 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import org.openapi4j.parser.model.v3.Callback;
 import org.openapi4j.parser.model.v3.Contact;
 import org.openapi4j.parser.model.v3.ExternalDocs;
@@ -47,4 +48,6 @@
   void sendServerVariableTraversals(ImmutableMap<String, ServerVariable> serverVariables);
 
   void sendTagTraversals(ImmutableList<Tag> tagList);
+
+  ImmutableSet<Extension> traverse();
 }
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/BaseExtensionScopeValidatorTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/BaseExtensionScopeValidatorTest.java
new file mode 100644
index 0000000..aab22ec
--- /dev/null
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/BaseExtensionScopeValidatorTest.java
@@ -0,0 +1,154 @@
+package com.apigee.security.oas.extendedvalidator;
+
+import static com.apigee.security.oas.extendedvalidator.ExtensionName.X_SECURITY_ALLOW;
+import static com.apigee.security.oas.extendedvalidator.ExtensionName.X_SECURITY_TYPE;
+import static com.apigee.security.oas.extendedvalidator.ExtensionName.X_SECURITY_TYPE_DEFINITIONS;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.when;
+
+import com.google.common.collect.ImmutableList;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.Optional;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.quality.Strictness;
+import org.openapi4j.parser.model.OpenApiSchema;
+import org.openapi4j.parser.model.v3.OpenApi3;
+import org.openapi4j.parser.model.v3.Operation;
+import org.openapi4j.parser.model.v3.Parameter;
+import org.openapi4j.parser.model.v3.Path;
+
+@RunWith(JUnit4.class)
+public class BaseExtensionScopeValidatorTest {
+
+  @Rule public final MockitoRule mockito = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
+
+  @Mock private Extension extension;
+
+  private ExtensionValidator validator;
+
+  /** Setting up injector and scope validator instances. */
+  @Before
+  public void setup() {
+    Injector injector = Guice.createInjector(new ExtendedValidatorMainModule());
+    validator = injector.getInstance(ExtensionScopeValidator.class);
+  }
+
+  private static ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>>
+      createPathList(Class<? extends OpenApiSchema>[] pathClasses) {
+    ArrayList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> list = new ArrayList<>();
+    for (Class<? extends OpenApiSchema> pathClass : pathClasses) {
+      list.add(new SimpleImmutableEntry<>(pathClass, Optional.empty()));
+    }
+
+    return ImmutableList.copyOf(list);
+  }
+
+  @Test
+  public void validate_unsupportedExtension_returnsUnsupportedExtensionErrorType() {
+    ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> path =
+        ImmutableList.of();
+    when(extension.getExtensionName()).thenReturn("x-unsupported");
+    when(extension.getExtensionPath()).thenReturn(path);
+
+    assertThat(validator.validate(extension))
+        .hasSize(1)
+        .first()
+        .extracting(ExtensionValidationMessage::type)
+        .isEqualTo("UNSUPPORTED_EXTENSION");
+  }
+
+  @Test
+  public void validate_emptyPathSecurityTypeExtension_returnsInvalidScopeErrorType() {
+    when(extension.getExtensionName()).thenReturn(X_SECURITY_TYPE.getExtensionName());
+    when(extension.getExtensionPath()).thenReturn(ImmutableList.of());
+
+    assertThat(validator.validate(extension))
+        .hasSize(1)
+        .first()
+        .extracting(ExtensionValidationMessage::type)
+        .isEqualTo("INVALID_SCOPE");
+  }
+
+  @Test
+  public void validate_validScopeSecurityTypeExtension_returnsEmptyErrorSet() {
+    ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> path =
+        createPathList(new Class[] {Path.class, Operation.class, Parameter.class});
+    when(extension.getExtensionName()).thenReturn(X_SECURITY_TYPE.getExtensionName());
+    when(extension.getExtensionPath()).thenReturn(path);
+
+    assertThat(validator.validate(extension)).isEmpty();
+  }
+
+  @Test
+  public void validate_invalidScopeSecurityTypeExtension_returnsInvalidScopeErrorType() {
+    ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> path =
+        createPathList(new Class[] {Path.class});
+    when(extension.getExtensionName()).thenReturn(X_SECURITY_TYPE.getExtensionName());
+    when(extension.getExtensionPath()).thenReturn(path);
+
+    assertThat(validator.validate(extension))
+        .hasSize(1)
+        .first()
+        .extracting(ExtensionValidationMessage::type)
+        .isEqualTo("INVALID_SCOPE");
+  }
+
+  @Test
+  public void validate_validScopeSecurityAllowExtension_returnsEmptyErrorSet() {
+    ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> path =
+        createPathList(new Class[] {Path.class, Operation.class});
+    when(extension.getExtensionName()).thenReturn(X_SECURITY_ALLOW.getExtensionName());
+    when(extension.getExtensionPath()).thenReturn(path);
+
+    assertThat(validator.validate(extension)).isEmpty();
+  }
+
+  @Test
+  public void validate_invalidScopeSecurityAllowExtension_returnsInvalidScopeErrorTypes() {
+    ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> path =
+        createPathList(new Class[] {Path.class});
+    when(extension.getExtensionName()).thenReturn(X_SECURITY_ALLOW.getExtensionName());
+    when(extension.getExtensionPath()).thenReturn(path);
+
+    assertThat(validator.validate(extension))
+        .hasSize(1)
+        .first()
+        .extracting(ExtensionValidationMessage::type)
+        .isEqualTo("INVALID_SCOPE");
+  }
+
+  @Test
+  public void validate_validScopeSecurityTypeDefinitionsExtension_returnsEmptyErrorSet() {
+    ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> path =
+        createPathList(new Class[] {OpenApi3.class});
+    when(extension.getExtensionName()).thenReturn(X_SECURITY_TYPE_DEFINITIONS.getExtensionName());
+    when(extension.getExtensionPath()).thenReturn(path);
+
+    assertThat(validator.validate(extension)).isEmpty();
+  }
+
+  @Test
+  public void validate_invalidScopeSecurityTypeDefinitionsExtension_returnsInvalidScopeErrorType() {
+    ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> path =
+        createPathList(new Class[] {OpenApi3.class, Path.class});
+    when(extension.getExtensionName()).thenReturn(X_SECURITY_TYPE_DEFINITIONS.getExtensionName());
+    when(extension.getExtensionPath()).thenReturn(path);
+
+    assertThat(validator.validate(extension))
+        .hasSize(1)
+        .first()
+        .extracting(ExtensionValidationMessage::type)
+        .isEqualTo("INVALID_SCOPE");
+  }
+}
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/CallbackTraversalTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/CallbackTraversalTest.java
index dc4944d..1acb831 100644
--- a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/CallbackTraversalTest.java
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/CallbackTraversalTest.java
@@ -1,12 +1,16 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import com.fasterxml.jackson.databind.JsonNode;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import java.util.Map;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -28,21 +32,18 @@
   private CallbackTraversal callbackTraversal;
 
   @Rule public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
+  @Mock private ExtensionFactory extensionFactory;
   @Mock private TraversalHelperFactory traversalHelperFactory;
   @Mock private TraversalHelper traversalHelper;
-  @Mock private ExtensionValidationIntegrator extensionValidationIntegrator;
   @Mock private Callback callback;
+  @Mock private Extension extension;
 
   /** Sets up live and mocked instances for testing. */
   @Before
   public void setup() {
     callbackTraversal =
         new CallbackTraversal(
-            traversalHelperFactory,
-            extensionValidationIntegrator,
-            ImmutableList.of(),
-            callback,
-            "name");
+            traversalHelperFactory, extensionFactory, ImmutableList.of(), callback, "name");
 
     when(callback.getCallbackPaths()).thenReturn(callbackPaths);
 
@@ -64,4 +65,20 @@
 
     verify(traversalHelper, atLeastOnce()).sendPathTraversals(any());
   }
+
+  @Test
+  public void traverse_withExtensions_returnsGeneratedExtensions() {
+    when(callback.getExtensions()).thenReturn(Map.of("x-callback", ""));
+    when(extensionFactory.create(anyString(), any(JsonNode.class), any(ImmutableList.class)))
+        .thenReturn(extension);
+
+    assertThat(callbackTraversal.traverse()).hasSize(1).containsOnlyOnce(extension);
+  }
+
+  @Test
+  public void traverse_withoutExtensions_returnsEmptyExtensionsImmutableList() {
+    when(callback.getExtensions()).thenReturn(Map.of());
+
+    assertThat(callbackTraversal.traverse()).isEmpty();
+  }
 }
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/InfoTraversalTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/InfoTraversalTest.java
index 462db49..17b5e8c 100644
--- a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/InfoTraversalTest.java
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/InfoTraversalTest.java
@@ -1,10 +1,14 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import com.fasterxml.jackson.databind.JsonNode;
 import com.google.common.collect.ImmutableList;
+import java.util.Map;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -25,7 +29,8 @@
 
   @Mock private TraversalHelperFactory traversalHelperFactory;
   @Mock private TraversalHelper traversalHelper;
-  @Mock private ExtensionValidationIntegrator extensionValidationIntegrator;
+  @Mock private ExtensionFactory extensionFactory;
+  @Mock private Extension extension;
   @Mock private Info info;
 
   private final License license = new License();
@@ -37,8 +42,7 @@
   @Before
   public void setup() {
     infoTraversal =
-        new InfoTraversal(
-            traversalHelperFactory, extensionValidationIntegrator, ImmutableList.of(), info);
+        new InfoTraversal(traversalHelperFactory, extensionFactory, ImmutableList.of(), info);
 
     when(info.getContact()).thenReturn(contact);
     when(info.getLicense()).thenReturn(license);
@@ -59,4 +63,20 @@
 
     verify(traversalHelper).sendLicenseTraversal(license);
   }
+
+  @Test
+  public void traverse_withExtensions_returnsGeneratedExtensions() {
+    when(info.getExtensions()).thenReturn(Map.of("x-info", ""));
+    when(extensionFactory.create(anyString(), any(JsonNode.class), any(ImmutableList.class)))
+        .thenReturn(extension);
+
+    assertThat(infoTraversal.traverse()).hasSize(1).containsOnlyOnce(extension);
+  }
+
+  @Test
+  public void traverse_withoutExtensions_returnsEmptyExtensionsImmutableList() {
+    when(info.getExtensions()).thenReturn(Map.of());
+
+    assertThat(infoTraversal.traverse()).isEmpty();
+  }
 }
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversalTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversalTest.java
index efe1ae1..f1375a0 100644
--- a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversalTest.java
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversalTest.java
@@ -1,12 +1,16 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import com.fasterxml.jackson.databind.JsonNode;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import java.util.Map;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -20,6 +24,7 @@
 import org.openapi4j.parser.model.v3.Info;
 import org.openapi4j.parser.model.v3.OpenApi3;
 import org.openapi4j.parser.model.v3.Path;
+import org.openapi4j.parser.model.v3.SecurityRequirement;
 import org.openapi4j.parser.model.v3.Server;
 import org.openapi4j.parser.model.v3.Tag;
 
@@ -30,8 +35,9 @@
 
   @Mock private TraversalHelperFactory traversalHelperFactory;
   @Mock private TraversalHelper traversalHelper;
-  @Mock private ExtensionValidationIntegrator extensionValidationIntegrator;
+  @Mock private ExtensionFactory extensionFactory;
   @Mock private OpenApi3 openApiSpec;
+  @Mock private Extension extension;
 
   private final Info info = new Info();
   private final ExternalDocs externalDocs = new ExternalDocs();
@@ -42,6 +48,9 @@
 
   private final ImmutableMap<String, Path> paths =
       ImmutableMap.of("one", new Path(), "two", new Path(), "three", new Path());
+  private final ImmutableList<SecurityRequirement> securityRequirements =
+      ImmutableList.of(
+          new SecurityRequirement(), new SecurityRequirement(), new SecurityRequirement());
 
   private OpenApiSpecificationTraversal openApiSpecificationTraversal;
 
@@ -49,14 +58,14 @@
   @Before
   public void setup() {
     openApiSpecificationTraversal =
-        new OpenApiSpecificationTraversal(
-            traversalHelperFactory, extensionValidationIntegrator, openApiSpec);
+        new OpenApiSpecificationTraversal(traversalHelperFactory, extensionFactory, openApiSpec);
 
     when(openApiSpec.getServers()).thenReturn(servers);
     when(openApiSpec.getTags()).thenReturn(tags);
     when(openApiSpec.getInfo()).thenReturn(info);
     when(openApiSpec.getExternalDocs()).thenReturn(externalDocs);
     when(openApiSpec.getPaths()).thenReturn(paths);
+    when(openApiSpec.getSecurityRequirements()).thenReturn(securityRequirements);
 
     when(traversalHelperFactory.create(any(ImmutableList.class))).thenReturn(traversalHelper);
   }
@@ -92,6 +101,22 @@
   }
 
   @Test
+  public void traverse_sendsSecurityRequirementsToTraversalHelper() {
+    openApiSpecificationTraversal.traverse();
+
+    verify(traversalHelper).sendSecurityRequirementTraversals(securityRequirements);
+  }
+
+  @Test
+  public void traverse_nullSecurityRequirementsMember_doesNotFail() {
+    when(openApiSpec.getSecurityRequirements()).thenReturn(null);
+
+    openApiSpecificationTraversal.traverse();
+
+    verify(traversalHelper, atLeastOnce()).sendSecurityRequirementTraversals(any());
+  }
+
+  @Test
   public void traverse_sendsServersToTraversalHelper() {
     openApiSpecificationTraversal.traverse();
 
@@ -122,4 +147,20 @@
 
     verify(traversalHelper, atLeastOnce()).sendTagTraversals(any());
   }
+
+  @Test
+  public void traverse_withExtensions_returnsGeneratedExtensions() {
+    when(openApiSpec.getExtensions()).thenReturn(Map.of("x-openapi", ""));
+    when(extensionFactory.create(anyString(), any(JsonNode.class), any(ImmutableList.class)))
+        .thenReturn(extension);
+
+    assertThat(openApiSpecificationTraversal.traverse()).hasSize(1).containsOnlyOnce(extension);
+  }
+
+  @Test
+  public void traverse_withoutExtensions_returnsEmptyExtensionsImmutableList() {
+    when(openApiSpec.getExtensions()).thenReturn(Map.of());
+
+    assertThat(openApiSpecificationTraversal.traverse()).isEmpty();
+  }
 }
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/OperationTraversalTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/OperationTraversalTest.java
index 5e4436e..1bd3800 100644
--- a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/OperationTraversalTest.java
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/OperationTraversalTest.java
@@ -1,12 +1,16 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import com.fasterxml.jackson.databind.JsonNode;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import java.util.Map;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -31,7 +35,8 @@
 
   @Mock private TraversalHelperFactory traversalHelperFactory;
   @Mock private TraversalHelper traversalHelper;
-  @Mock private ExtensionValidationIntegrator extensionValidationIntegrator;
+  @Mock private ExtensionFactory extensionFactory;
+  @Mock private Extension extension;
   @Mock private Operation operation;
 
   private final ExternalDocs externalDocs = new ExternalDocs();
@@ -53,7 +58,6 @@
   /** Sets up live and mocked instances for testing. */
   @Before
   public void setup() {
-
     when(operation.getExternalDocs()).thenReturn(externalDocs);
     when(operation.getServers()).thenReturn(servers);
     when(operation.getParameters()).thenReturn(parameters);
@@ -63,11 +67,7 @@
 
     operationTraversal =
         new OperationTraversal(
-            traversalHelperFactory,
-            extensionValidationIntegrator,
-            ImmutableList.of(),
-            operation,
-            "name");
+            traversalHelperFactory, extensionFactory, ImmutableList.of(), operation, "name");
 
     when(traversalHelperFactory.create(any(ImmutableList.class))).thenReturn(traversalHelper);
   }
@@ -158,4 +158,20 @@
 
     verify(traversalHelper, atLeastOnce()).sendServerTraversals(any());
   }
+
+  @Test
+  public void traverse_withExtensions_returnsGeneratedExtensions() {
+    when(operation.getExtensions()).thenReturn(Map.of("x-operation", ""));
+    when(extensionFactory.create(anyString(), any(JsonNode.class), any(ImmutableList.class)))
+        .thenReturn(extension);
+
+    assertThat(operationTraversal.traverse()).hasSize(1).containsOnlyOnce(extension);
+  }
+
+  @Test
+  public void traverse_withoutExtensions_returnsEmptyExtensionsImmutableList() {
+    when(operation.getExtensions()).thenReturn(Map.of());
+
+    assertThat(operationTraversal.traverse()).isEmpty();
+  }
 }
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/PathTraversalTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/PathTraversalTest.java
index 042f0a4..3bc0518 100644
--- a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/PathTraversalTest.java
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/PathTraversalTest.java
@@ -1,12 +1,16 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import com.fasterxml.jackson.databind.JsonNode;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import java.util.Map;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -37,7 +41,8 @@
   @Rule public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
   @Mock private TraversalHelperFactory traversalHelperFactory;
   @Mock private TraversalHelper traversalHelper;
-  @Mock private ExtensionValidationIntegrator extensionValidationIntegrator;
+  @Mock private ExtensionFactory extensionFactory;
+  @Mock private Extension extension;
   @Mock private Path path;
 
   /** Sets up live and mocked instances for testing. */
@@ -45,11 +50,7 @@
   public void setup() {
     pathTraversal =
         new PathTraversal(
-            traversalHelperFactory,
-            extensionValidationIntegrator,
-            ImmutableList.of(),
-            path,
-            "name");
+            traversalHelperFactory, extensionFactory, ImmutableList.of(), path, "name");
 
     when(path.getOperations()).thenReturn(operations);
     when(path.getParameters()).thenReturn(parameters);
@@ -105,4 +106,20 @@
 
     verify(traversalHelper, atLeastOnce()).sendServerTraversals(any());
   }
+
+  @Test
+  public void traverse_withExtensions_returnsGeneratedExtensions() {
+    when(path.getExtensions()).thenReturn(Map.of("x-path", ""));
+    when(extensionFactory.create(anyString(), any(JsonNode.class), any(ImmutableList.class)))
+        .thenReturn(extension);
+
+    assertThat(pathTraversal.traverse()).hasSize(1).containsOnlyOnce(extension);
+  }
+
+  @Test
+  public void traverse_withoutExtensions_returnsEmptyExtensionsImmutableList() {
+    when(path.getExtensions()).thenReturn(Map.of());
+
+    assertThat(pathTraversal.traverse()).isEmpty();
+  }
 }
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ServerTraversalTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ServerTraversalTest.java
index 5887005..b77c054 100644
--- a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ServerTraversalTest.java
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ServerTraversalTest.java
@@ -1,12 +1,16 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import com.fasterxml.jackson.databind.JsonNode;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import java.util.Map;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -26,7 +30,8 @@
 
   @Mock private TraversalHelperFactory traversalHelperFactory;
   @Mock private TraversalHelper traversalHelper;
-  @Mock private ExtensionValidationIntegrator extensionValidationIntegrator;
+  @Mock private ExtensionFactory extensionFactory;
+  @Mock private Extension extension;
   @Mock private Server server;
   @Mock private ServerVariable serverVariable;
 
@@ -37,8 +42,7 @@
   @Before
   public void setup() {
     serverTraversal =
-        new ServerTraversal(
-            traversalHelperFactory, extensionValidationIntegrator, ImmutableList.of(), server);
+        new ServerTraversal(traversalHelperFactory, extensionFactory, ImmutableList.of(), server);
 
     serverVariables =
         ImmutableMap.of(
@@ -66,4 +70,20 @@
 
     verify(traversalHelper, atLeastOnce()).sendServerVariableTraversals(any());
   }
+
+  @Test
+  public void traverse_withExtensions_returnsGeneratedExtensions() {
+    when(server.getExtensions()).thenReturn(Map.of("x-server", ""));
+    when(extensionFactory.create(anyString(), any(JsonNode.class), any(ImmutableList.class)))
+        .thenReturn(extension);
+
+    assertThat(serverTraversal.traverse()).hasSize(1).containsOnlyOnce(extension);
+  }
+
+  @Test
+  public void traverse_withoutExtensions_returnsEmptyExtensionsImmutableList() {
+    when(server.getExtensions()).thenReturn(Map.of());
+
+    assertThat(serverTraversal.traverse()).isEmpty();
+  }
 }
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TagTraversalTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TagTraversalTest.java
index ab86592..54ec074 100644
--- a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TagTraversalTest.java
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TagTraversalTest.java
@@ -1,10 +1,14 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import com.fasterxml.jackson.databind.JsonNode;
 import com.google.common.collect.ImmutableList;
+import java.util.Map;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -24,7 +28,8 @@
 
   @Mock private TraversalHelperFactory traversalHelperFactory;
   @Mock private TraversalHelper traversalHelper;
-  @Mock private ExtensionValidationIntegrator extensionValidationIntegrator;
+  @Mock private ExtensionFactory extensionFactory;
+  @Mock private Extension extension;
   @Mock private Tag tag;
 
   private final ExternalDocs externalDocs = new ExternalDocs();
@@ -35,8 +40,7 @@
   @Before
   public void setup() {
     tagTraversal =
-        new TagTraversal(
-            traversalHelperFactory, extensionValidationIntegrator, ImmutableList.of(), tag);
+        new TagTraversal(traversalHelperFactory, extensionFactory, ImmutableList.of(), tag);
 
     when(tag.getExternalDocs()).thenReturn(externalDocs);
     when(traversalHelperFactory.create(any(ImmutableList.class))).thenReturn(traversalHelper);
@@ -48,4 +52,20 @@
 
     verify(traversalHelper).sendExternalDocsTraversal(externalDocs);
   }
+
+  @Test
+  public void traverse_withExtensions_returnsGeneratedExtensions() {
+    when(tag.getExtensions()).thenReturn(Map.of("x-tag", ""));
+    when(extensionFactory.create(anyString(), any(JsonNode.class), any(ImmutableList.class)))
+        .thenReturn(extension);
+
+    assertThat(tagTraversal.traverse()).hasSize(1).containsOnlyOnce(extension);
+  }
+
+  @Test
+  public void traverse_withoutExtensions_returnsEmptyExtensionsImmutableList() {
+    when(tag.getExtensions()).thenReturn(Map.of());
+
+    assertThat(tagTraversal.traverse()).isEmpty();
+  }
 }
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TraversalTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TraversalTest.java
index e99a7d3..9df32b5 100644
--- a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TraversalTest.java
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TraversalTest.java
@@ -23,7 +23,7 @@
   @Rule public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
 
   @Mock private TraversalHelperFactory traversalHelperFactory;
-  @Mock private ExtensionValidationIntegrator baseExtensionValidationIntegrator;
+  @Mock private ExtensionFactory extensionFactory;
 
   @Test
   public void instantiate_traversal_addsTraversalsOpenApiObjClassToTraversalPath() {
@@ -31,11 +31,7 @@
     ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> prevTraversalPath =
         ImmutableList.copyOf(new ArrayDeque<>());
     InfoTraversal traversal =
-        new InfoTraversal(
-            traversalHelperFactory,
-            baseExtensionValidationIntegrator,
-            prevTraversalPath,
-            new Info());
+        new InfoTraversal(traversalHelperFactory, extensionFactory, prevTraversalPath, new Info());
 
     ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>>
         updatedTraversalPath = traversal.getUpdatedTraversalPath();