Instruction:
作为 Java 安全专家，检测此代码中的安全漏洞：
## 内容1
### 文件路径：
etc/security/mh_default_org.xml
### 代码内容：
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:sec="http://www.springframework.org/schema/security"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:osgi="http://www.springframework.org/schema/osgi"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/osgi
       http://www.springframework.org/schema/osgi/spring-osgi.xsd
       http://www.springframework.org/schema/security
       http://www.springframework.org/schema/security/spring-security-3.1.xsd">

  <!-- ######################################## -->
  <!-- # Open and unsecured url patterns      # -->
  <!-- ######################################## -->

  <sec:http pattern="/admin/img/**" security="none" />
  <sec:http pattern="/favicon.ico" security="none" />
  <sec:http pattern="/img/**" security="none" />
  <sec:http pattern="/js/**" security="none" />
  <sec:http pattern="/lib/**" security="none" />
  <sec:http pattern="/admin-ng/fonts/**" security="none" />
  <sec:http pattern="/admin-ng/scripts/**" security="none" />
  <sec:http pattern="/admin-ng/styles/**" security="none" />
  <sec:http pattern="/play/*" security="none" />

  <sec:http create-session="ifRequired" servlet-api-provision="true" realm="Opencast"
    entry-point-ref="opencastEntryPoint">

    <!-- ################ -->
    <!-- # URL SECURITY # -->
    <!-- ################ -->

    <!-- Allow anonymous access to the login form -->
    <sec:intercept-url pattern="/admin-ng/login.html" access="ROLE_ANONYMOUS" />
    <sec:intercept-url pattern="/sysinfo/bundles/version" method="GET" access="ROLE_ANONYMOUS" />

    <!-- Protect admin UI facade -->
    <sec:intercept-url pattern="/workflow/definitions.json" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_CREATE" />
    <sec:intercept-url pattern="/admin-ng/acl/acls.json" method="GET" access="ROLE_ADMIN, ROLE_UI_ACLS_VIEW" />
    <sec:intercept-url pattern="/admin-ng/acl/*" method="GET" access="ROLE_ADMIN, ROLE_UI_ACLS_VIEW" />
    <sec:intercept-url pattern="/acl-manager/acl/*" method="GET" access="ROLE_ADMIN, ROLE_UI_ACLS_VIEW" />
    <sec:intercept-url pattern="/admin-ng/capture-agents/agents.json" method="GET" access="ROLE_ADMIN, ROLE_UI_LOCATIONS_VIEW, ROLE_UI_EVENTS_CREATE" />
    <sec:intercept-url pattern="/admin-ng/capture-agents/*" method="GET" access="ROLE_ADMIN, ROLE_UI_LOCATIONS_DETAILS_CAPABILITIES_VIEW, ROLE_UI_LOCATIONS_DETAILS_CONFIGURATION_VIEW, ROLE_UI_LOCATIONS_DETAILS_GENERAL_VIEW" />
    <sec:intercept-url pattern="/admin-ng/event/*/asset/assets.json" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_ASSETS_VIEW" />
    <sec:intercept-url pattern="/admin-ng/event/*/asset/attachment/*.json" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_ASSETS_VIEW" />
    <sec:intercept-url pattern="/admin-ng/event/*/asset/catalog/*.json" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_ASSETS_VIEW" />
    <sec:intercept-url pattern="/admin-ng/event/*/asset/media/*.json" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_ASSETS_VIEW" />
    <sec:intercept-url pattern="/admin-ng/event/*/asset/publication/*.json" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_ASSETS_VIEW" />
    <sec:intercept-url pattern="/admin-ng/event/catalogAdapters" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_METADATA_VIEW" />
    <sec:intercept-url pattern="/admin-ng/event/events.json" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_VIEW" />
    <sec:intercept-url pattern="/admin-ng/event/workflowProperties" method="POST" access="ROLE_ADMIN, ROLE_UI_EVENTS_CREATE, ROLE_UI_TASKS_CREATE, ROLE_UI_EVENTS_EDITOR_VIEW" />
    <sec:intercept-url pattern="/admin-ng/event/new/metadata" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_METADATA_VIEW" />
    <sec:intercept-url pattern="/admin-ng/event/new/processing" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_CREATE, ROLE_UI_TASKS_CREATE" />
    <sec:intercept-url pattern="/admin-ng/event/*/attachments.json" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_ATTACHMENTS_VIEW" />
    <sec:intercept-url pattern="/admin-ng/event/*/comments" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_COMMENTS_VIEW"/>
    <sec:intercept-url pattern="/admin-ng/event/*/comment/*" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_COMMENTS_VIEW"/>
    <sec:intercept-url pattern="/admin-ng/event/*/publications.json" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_PUBLICATIONS_VIEW" />
    <sec:intercept-url pattern="/admin-ng/event/*/hasActiveTransaction" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_VIEW" />
    <sec:intercept-url pattern="/admin-ng/event/*/hasSnapshots.json" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_DELETE" />
    <sec:intercept-url pattern="/admin-ng/event/*/media.json" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_MEDIA_VIEW"/>
    <sec:intercept-url pattern="/admin-ng/event/*/media/*.json" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_MEDIA_VIEW"/>
    <sec:intercept-url pattern="/admin-ng/event/*/metadata.json" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_METADATA_VIEW" />
    <sec:intercept-url pattern="/admin-ng/event/*/scheduling.json" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_SCHEDULING_VIEW" />
    <sec:intercept-url pattern="/admin-ng/event/*/workflows.json" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_WORKFLOWS_VIEW" />
    <sec:intercept-url pattern="/admin-ng/event/*/workflows/*/operations.json" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_WORKFLOWS_VIEW"/>
    <sec:intercept-url pattern="/admin-ng/event/*/workflows/*/operations/*" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_WORKFLOWS_VIEW"/>
    <sec:intercept-url pattern="/admin-ng/event/*/workflows/*/errors.json" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_WORKFLOWS_VIEW"/>
    <sec:intercept-url pattern="/admin-ng/event/*/workflows/*/errors/*.json" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_WORKFLOWS_VIEW"/>
    <sec:intercept-url pattern="/admin-ng/event/*/workflows/*" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_WORKFLOWS_VIEW"/>
    <sec:intercept-url pattern="/admin-ng/event/*/access.json" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_ACL_VIEW" />
    <sec:intercept-url pattern="/admin-ng/event/*" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_VIEW" />
    <sec:intercept-url pattern="/admin-ng/groups/groups.json" method="GET" access="ROLE_ADMIN, ROLE_UI_GROUPS_VIEW" />
    <sec:intercept-url pattern="/admin-ng/groups/*" method="GET" access="ROLE_ADMIN, ROLE_UI_GROUPS_EDIT" />
    <sec:intercept-url pattern="/admin-ng/job/jobs.json" method="GET" access="ROLE_ADMIN, ROLE_UI_JOBS_VIEW" />
    <sec:intercept-url pattern="/admin-ng/series/series.json" method="GET" access="ROLE_ADMIN, ROLE_UI_SERIES_VIEW" />
    <sec:intercept-url pattern="/admin-ng/series/new/metadata" method="GET" access="ROLE_ADMIN, ROLE_UI_SERIES_DETAILS_METADATA_VIEW" />
    <sec:intercept-url pattern="/admin-ng/series/new/themes" method="GET" access="ROLE_ADMIN, ROLE_UI_SERIES_DETAILS_THEMES_EDIT" />
    <sec:intercept-url pattern="/admin-ng/series/*/access.json" method="GET" access="ROLE_ADMIN, ROLE_UI_SERIES_DETAILS_ACL_VIEW" />
    <sec:intercept-url pattern="/admin-ng/series/*/metadata.json" method="GET" access="ROLE_ADMIN, ROLE_UI_SERIES_DETAILS_METADATA_VIEW" />
    <sec:intercept-url pattern="/admin-ng/series/*/theme.json" method="GET" access="ROLE_ADMIN, ROLE_UI_SERIES_DETAILS_THEMES_VIEW" />
    <sec:intercept-url pattern="/admin-ng/server/servers.json" method="GET" access="ROLE_ADMIN, ROLE_UI_SERVERS_VIEW" />
    <sec:intercept-url pattern="/admin-ng/services/services.json" method="GET" access="ROLE_ADMIN, ROLE_UI_SERVICES_VIEW" />
    <sec:intercept-url pattern="/admin-ng/tasks/processing.json" method="GET" access="ROLE_ADMIN, ROLE_UI_TASKS_CREATE" />
    <sec:intercept-url pattern="/admin-ng/themes/themes.json" method="GET" access="ROLE_ADMIN, ROLE_UI_THEMES_VIEW" />
    <sec:intercept-url pattern="/admin-ng/themes/*/usage.json" method="GET" access="ROLE_ADMIN, ROLE_UI_THEMES_DETAILS_VIEW" />
    <sec:intercept-url pattern="/admin-ng/themes/*.json" method="GET" access="ROLE_ADMIN, ROLE_UI_THEMES_DETAILS_VIEW" />
    <sec:intercept-url pattern="/admin-ng/tools/*/editor.json" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_EDITOR_VIEW" />
    <sec:intercept-url pattern="/admin-ng/tools/*/thumbnail.json" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_EDITOR_VIEW" />
    <sec:intercept-url pattern="/admin-ng/tools/*.json" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_EDITOR_VIEW" />
    <sec:intercept-url pattern="/admin-ng/users/users.json" method="GET" access="ROLE_ADMIN, ROLE_UI_USERS_VIEW" />
    <sec:intercept-url pattern="/admin-ng/users/*.json" method="GET" access="ROLE_ADMIN, ROLE_UI_USERS_EDIT" />
    <sec:intercept-url pattern="/admin-ng/user-settings/signature" method="GET" access="ROLE_ADMIN, ROLE_ADMIN_UI" />
    <sec:intercept-url pattern="/admin-ng/resources/events/filters.json" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_VIEW" />
    <sec:intercept-url pattern="/admin-ng/resources/series/filters.json" method="GET" access="ROLE_ADMIN, ROLE_UI_SERIES_VIEW" />
    <sec:intercept-url pattern="/admin-ng/resources/themes/filters.json" method="GET" access="ROLE_ADMIN, ROLE_UI_THEMES_VIEW" />
    <sec:intercept-url pattern="/admin-ng/resources/recordings/filters.json" method="GET" access="ROLE_ADMIN, ROLE_UI_LOCATIONS_VIEW" />
    <sec:intercept-url pattern="/admin-ng/resources/users/filters.json" method="GET" access="ROLE_ADMIN, ROLE_UI_USERS_VIEW" />
    <sec:intercept-url pattern="/admin-ng/resources/groups/filters.json" method="GET" access="ROLE_ADMIN, ROLE_UI_GROUPS_VIEW" />
    <sec:intercept-url pattern="/admin-ng/resources/components.json" method="GET" access="ROLE_ADMIN, ROLE_ADMIN_UI" />
    <sec:intercept-url pattern="/admin-ng/resources/providers.json" method="GET" access="ROLE_ADMIN, ROLE_ADMIN_UI" />
    <sec:intercept-url pattern="/admin-ng/resources/THEMES.json" method="GET" access="ROLE_ADMIN, ROLE_UI_THEMES_VIEW" />
    <sec:intercept-url pattern="/admin-ng/resources/*.json" method="GET" access="ROLE_ADMIN, ROLE_ADMIN_UI" />
    <sec:intercept-url pattern="/assets/assets/**" method="GET" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_ASSETS_VIEW" />

    <sec:intercept-url pattern="/admin-ng/acl/*" method="PUT" access="ROLE_ADMIN, ROLE_UI_ACLS_EDIT" />
    <sec:intercept-url pattern="/admin-ng/event/*/metadata" method="PUT" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_METADATA_EDIT" />
    <sec:intercept-url pattern="/admin-ng/event/*/scheduling" method="PUT" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_SCHEDULING_EDIT" />
    <sec:intercept-url pattern="/admin-ng/event/*/workflows" method="PUT" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_WORKFLOWS_EDIT" />
    <sec:intercept-url pattern="/admin-ng/event/*/workflows/*/action/stop" method="PUT" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_WORKFLOWS_EDIT" />
    <sec:intercept-url pattern="/admin-ng/event/*/workflows/*/action/retry" method="PUT" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_WORKFLOWS_EDIT" />
    <sec:intercept-url pattern="/admin-ng/event/*/workflows/*/action/none" method="PUT" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_WORKFLOWS_EDIT" />
    <sec:intercept-url pattern="/admin-ng/event/*/comment/*/*" method="PUT" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_COMMENTS_EDIT" />
    <sec:intercept-url pattern="/admin-ng/event/*/comment/*" method="PUT" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_COMMENTS_EDIT"/>
    <sec:intercept-url pattern="/admin-ng/event/bulk/update" method="PUT" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_SCHEDULING_EDIT" />
    <sec:intercept-url pattern="/admin-ng/groups/*" method="PUT" access="ROLE_ADMIN, ROLE_UI_GROUPS_EDIT" />
    <sec:intercept-url pattern="/admin-ng/series/*/metadata" method="PUT" access="ROLE_ADMIN, ROLE_UI_SERIES_DETAILS_METADATA_EDIT" />
    <sec:intercept-url pattern="/admin-ng/series/*/theme" method="PUT" access="ROLE_ADMIN, ROLE_UI_SERIES_DETAILS_THEMES_EDIT" />
    <sec:intercept-url pattern="/admin-ng/themes/*" method="PUT" access="ROLE_ADMIN, ROLE_UI_THEMES_EDIT" />
    <sec:intercept-url pattern="/admin-ng/users/*" method="PUT" access="ROLE_ADMIN, ROLE_UI_USERS_EDIT" />
    <sec:intercept-url pattern="/admin-ng/user-settings/signature/*" method="PUT" access="ROLE_ADMIN, ROLE_ADMIN_UI" />

    <sec:intercept-url pattern="/services/maintenance" method="POST" access="ROLE_ADMIN, ROLE_UI_SERVERS_MAINTENANCE_EDIT" />
    <sec:intercept-url pattern="/services/sanitize" method="POST" access="ROLE_ADMIN, ROLE_UI_SERVICES_STATUS_EDIT" />
    <sec:intercept-url pattern="/staticfiles" method="POST" access="ROLE_ADMIN, ROLE_UI_THEMES_CREATE, ROLE_UI_THEMES_EDIT" />
    <sec:intercept-url pattern="/admin-ng/acl" method="POST" access="ROLE_ADMIN, ROLE_UI_ACLS_CREATE" />
    <sec:intercept-url pattern="/admin-ng/event/bulk/conflicts" method="POST" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_SCHEDULING_EDIT" />
    <sec:intercept-url pattern="/admin-ng/event/deleteEvents" method="POST" access="ROLE_ADMIN, ROLE_UI_EVENTS_DELETE" />
    <sec:intercept-url pattern="/admin-ng/event/new" method="POST" access="ROLE_ADMIN, ROLE_UI_EVENTS_CREATE" />
    <sec:intercept-url pattern="/admin-ng/event/new/conflicts" method="POST" access="ROLE_ADMIN, ROLE_UI_EVENTS_CREATE" />
    <sec:intercept-url pattern="/admin-ng/event/scheduling.json" method="POST" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_SCHEDULING_VIEW" />
    <sec:intercept-url pattern="/admin-ng/event/*/access" method="POST" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_ACL_EDIT" />
    <sec:intercept-url pattern="/admin-ng/event/*/assets" method="POST" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_ASSETS_EDIT" />
    <sec:intercept-url pattern="/admin-ng/event/*/comment" method="POST" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_COMMENTS_CREATE" />
    <sec:intercept-url pattern="/admin-ng/event/*/comment/*/reply" method="POST" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_COMMENTS_CREATE" />
    <sec:intercept-url pattern="/admin-ng/event/*/comment/*" method="POST" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_COMMENTS_CREATE" />
    <sec:intercept-url pattern="/admin-ng/groups" method="POST" access="ROLE_ADMIN, ROLE_UI_GROUPS_CREATE" />
    <sec:intercept-url pattern="/admin-ng/index/*" method="POST" access="ROLE_ADMIN" />
    <sec:intercept-url pattern="/admin-ng/series/deleteSeries" method="POST" access="ROLE_ADMIN, ROLE_UI_SERIES_DELETE" />
    <sec:intercept-url pattern="/admin-ng/series/new" method="POST" access="ROLE_ADMIN, ROLE_UI_SERIES_CREATE" />
    <sec:intercept-url pattern="/admin-ng/series/*/access" method="POST" access="ROLE_ADMIN, ROLE_UI_SERIES_DETAILS_ACL_EDIT" />
    <sec:intercept-url pattern="/admin-ng/tasks/new" method="POST" access="ROLE_ADMIN, ROLE_UI_TASKS_CREATE" />
    <sec:intercept-url pattern="/admin-ng/themes" method="POST" access="ROLE_ADMIN, ROLE_UI_THEMES_CREATE" />
    <sec:intercept-url pattern="/admin-ng/tools/*/editor.json" method="POST" access="ROLE_ADMIN, ROLE_UI_EVENTS_EDITOR_EDIT" />
    <sec:intercept-url pattern="/admin-ng/tools/*/thumbnail.json" method="POST" access="ROLE_ADMIN, ROLE_UI_EVENTS_EDITOR_EDIT" />
    <sec:intercept-url pattern="/admin-ng/users" method="POST" access="ROLE_ADMIN, ROLE_UI_USERS_CREATE" />
    <sec:intercept-url pattern="/admin-ng/user-settings/signature" method="POST" access="ROLE_ADMIN, ROLE_ADMIN_UI" />

    <sec:intercept-url pattern="/admin-ng/acl/*" method="DELETE" access="ROLE_ADMIN, ROLE_UI_ACLS_DELETE" />
    <sec:intercept-url pattern="/admin-ng/capture-agents/*" method="DELETE" access="ROLE_ADMIN, ROLE_UI_LOCATIONS_DELETE" />
    <sec:intercept-url pattern="/admin-ng/event/*/comment/*/*" method="DELETE" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_COMMENTS_DELETE" />
    <sec:intercept-url pattern="/admin-ng/event/*/comment/*" method="DELETE" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_COMMENTS_DELETE" />
    <sec:intercept-url pattern="/admin-ng/event/*/workflows/*" method="DELETE" access="ROLE_ADMIN, ROLE_UI_EVENTS_DETAILS_WORKFLOWS_DELETE" />
    <sec:intercept-url pattern="/admin-ng/event/*" method="DELETE" access="ROLE_ADMIN, ROLE_UI_EVENTS_DELETE" />
    <sec:intercept-url pattern="/admin-ng/groups/*" method="DELETE" access="ROLE_ADMIN, ROLE_UI_GROUPS_DELETE" />
    <sec:intercept-url pattern="/admin-ng/series/*/theme" method="DELETE" access="ROLE_ADMIN, ROLE_UI_SERIES_DETAILS_THEMES_EDIT" />
    <sec:intercept-url pattern="/admin-ng/series/*" method="DELETE" access="ROLE_ADMIN, ROLE_UI_SERIES_DELETE" />
    <sec:intercept-url pattern="/admin-ng/themes/*" method="DELETE" access="ROLE_ADMIN, ROLE_UI_THEMES_DELETE" />
    <sec:intercept-url pattern="/admin-ng/users/*" method="DELETE" access="ROLE_ADMIN, ROLE_UI_USERS_DELETE" />

    <!-- Securing the URLs for the external API interface -->
    <!-- External API GET Endpoints -->
    <sec:intercept-url pattern="/api" method="GET" access="ROLE_ADMIN, ROLE_API"/>
    <sec:intercept-url pattern="/api/agents" method="GET" access="ROLE_ADMIN, ROLE_API_CAPTURE_AGENTS_VIEW"/>
    <sec:intercept-url pattern="/api/agents/*" method="GET" access="ROLE_ADMIN, ROLE_API_CAPTURE_AGENTS_VIEW"/>
    <sec:intercept-url pattern="/api/events" method="GET" access="ROLE_ADMIN, ROLE_API_EVENTS_VIEW"/>
    <sec:intercept-url pattern="/api/events/*" method="GET" access="ROLE_ADMIN, ROLE_API_EVENTS_VIEW"/>
    <sec:intercept-url pattern="/api/events/*/acl" method="GET" access="ROLE_ADMIN, ROLE_API_EVENTS_ACL_VIEW"/>
    <sec:intercept-url pattern="/api/events/*/media" method="GET" access="ROLE_ADMIN, ROLE_API_EVENTS_MEDIA_VIEW"/>
    <sec:intercept-url pattern="/api/events/*/media/*" method="GET" access="ROLE_ADMIN, ROLE_API_EVENTS_MEDIA_VIEW"/>
    <sec:intercept-url pattern="/api/events/*/metadata" method="GET" access="ROLE_ADMIN, ROLE_API_EVENTS_METADATA_VIEW"/>
    <sec:intercept-url pattern="/api/events/*/metadata/*" method="GET" access="ROLE_ADMIN, ROLE_API_EVENTS_METADATA_VIEW"/>
    <sec:intercept-url pattern="/api/events/*/publications" method="GET" access="ROLE_ADMIN, ROLE_API_EVENTS_PUBLICATIONS_VIEW"/>
    <sec:intercept-url pattern="/api/events/*/publications/*" method="GET" access="ROLE_ADMIN, ROLE_API_EVENTS_PUBLICATIONS_VIEW"/>
    <sec:intercept-url pattern="/api/events/*/scheduling" method="GET" access="ROLE_ADMIN, ROLE_API_EVENTS_SCHEDULING_VIEW"/>
    <sec:intercept-url pattern="/api/groups" method="GET" access="ROLE_ADMIN, ROLE_API_GROUPS_VIEW"/>
    <sec:intercept-url pattern="/api/groups/*" method="GET" access="ROLE_ADMIN, ROLE_API_GROUPS_VIEW"/>
    <sec:intercept-url pattern="/api/info/*" method="GET" access="ROLE_ADMIN, ROLE_API" />
    <sec:intercept-url pattern="/api/info/me/*" method="GET" access="ROLE_ADMIN, ROLE_API" />
    <sec:intercept-url pattern="/api/series" method="GET" access="ROLE_ADMIN, ROLE_API_SERIES_VIEW"/>
    <sec:intercept-url pattern="/api/series/*" method="GET" access="ROLE_ADMIN, ROLE_API_SERIES_VIEW"/>
    <sec:intercept-url pattern="/api/series/*/acl" method="GET" access="ROLE_ADMIN, ROLE_API_SERIES_ACL_VIEW"/>
    <sec:intercept-url pattern="/api/series/*/metadata" method="GET" access="ROLE_ADMIN, ROLE_API_SERIES_METADATA_VIEW"/>
    <sec:intercept-url pattern="/api/series/*/metadata/*" method="GET" access="ROLE_ADMIN, ROLE_API_SERIES_METADATA_VIEW"/>
    <sec:intercept-url pattern="/api/series/*/properties" method="GET" access="ROLE_ADMIN, ROLE_API_SERIES_PROPERTIES_VIEW"/>
    <sec:intercept-url pattern="/api/version" method="GET" access="ROLE_ADMIN, ROLE_API"/>
    <sec:intercept-url pattern="/api/version/*" method="GET" access="ROLE_ADMIN, ROLE_API"/>
    <sec:intercept-url pattern="/api/workflows" method="GET" access="ROLE_ADMIN, ROLE_API_WORKFLOW_INSTANCE_VIEW"/>
    <sec:intercept-url pattern="/api/workflows/*" method="GET" access="ROLE_ADMIN, ROLE_API_WORKFLOW_INSTANCE_VIEW"/>
    <sec:intercept-url pattern="/api/workflow-definitions" method="GET" access="ROLE_ADMIN, ROLE_API_WORKFLOW_DEFINITION_VIEW"/>
    <sec:intercept-url pattern="/api/workflow-definitions/*" method="GET" access="ROLE_ADMIN, ROLE_API_WORKFLOW_DEFINITION_VIEW"/>
    <!-- External API PUT Endpoints -->
    <sec:intercept-url pattern="/api/events/*" method="PUT" access="ROLE_ADMIN, ROLE_API_EVENTS_EDIT"/>
    <sec:intercept-url pattern="/api/events/*/acl" method="PUT" access="ROLE_ADMIN, ROLE_API_EVENTS_ACL_EDIT"/>
    <sec:intercept-url pattern="/api/events/*/metadata" method="PUT" access="ROLE_ADMIN, ROLE_API_EVENTS_METADATA_EDIT"/>
    <sec:intercept-url pattern="/api/events/*/metadata/*" method="PUT" access="ROLE_ADMIN, ROLE_API_EVENTS_METADATA_EDIT"/>
    <sec:intercept-url pattern="/api/events/*/scheduling" method="PUT" access="ROLE_ADMIN, ROLE_API_EVENTS_SCHEDULING_EDIT"/>
    <sec:intercept-url pattern="/api/groups/*" method="PUT" access="ROLE_ADMIN, ROLE_API_GROUPS_EDIT"/>
    <sec:intercept-url pattern="/api/series/*" method="PUT" access="ROLE_ADMIN, ROLE_API_SERIES_EDIT"/>
    <sec:intercept-url pattern="/api/series/*/acl" method="PUT" access="ROLE_ADMIN, ROLE_API_SERIES_ACL_EDIT"/>
    <sec:intercept-url pattern="/api/series/*/metadata" method="PUT" access="ROLE_ADMIN, ROLE_API_SERIES_METADATA_EDIT"/>
    <sec:intercept-url pattern="/api/series/*/metadata/*" method="PUT" access="ROLE_ADMIN, ROLE_API_SERIES_METADATA_EDIT"/>
    <sec:intercept-url pattern="/api/series/*/properties" method="PUT" access="ROLE_ADMIN, ROLE_API_SERIES_PROPERTIES_EDIT"/>
    <sec:intercept-url pattern="/api/workflows/*" method="PUT" access="ROLE_ADMIN, ROLE_API_WORKFLOW_INSTANCE_EDIT"/>
    <!-- External API POST Endpoints -->
    <sec:intercept-url pattern="/api/events" method="POST" access="ROLE_ADMIN, ROLE_API_EVENTS_CREATE"/>
    <sec:intercept-url pattern="/api/events/*" method="POST" access="ROLE_ADMIN, ROLE_API_EVENTS_EDIT"/>
    <sec:intercept-url pattern="/api/events/*/acl/*" method="POST" access="ROLE_ADMIN, ROLE_API_EVENTS_ACL_EDIT"/>
    <sec:intercept-url pattern="/api/groups" method="POST" access="ROLE_ADMIN, ROLE_API_GROUPS_CREATE"/>
    <sec:intercept-url pattern="/api/groups/*/members/*" method="POST" access="ROLE_ADMIN, ROLE_API_GROUPS_EDIT"/>
    <sec:intercept-url pattern="/api/clearIndex" method="POST" access="ROLE_ADMIN"/>
    <sec:intercept-url pattern="/api/recreateIndex" method="POST" access="ROLE_ADMIN"/>
    <sec:intercept-url pattern="/api/recreateIndex/*" method="POST" access="ROLE_ADMIN"/>
    <sec:intercept-url pattern="/api/series" method="POST" access="ROLE_ADMIN, ROLE_API_SERIES_CREATE"/>
    <sec:intercept-url pattern="/api/security/sign" method="POST" access="ROLE_ADMIN, ROLE_API_SECURITY_EDIT"/>
    <sec:intercept-url pattern="/api/workflows" method="POST" access="ROLE_ADMIN, ROLE_API_WORKFLOW_INSTANCE_CREATE"/>
    <!-- External API DELETE Endpoints -->
    <sec:intercept-url pattern="/api/events/*" method="DELETE" access="ROLE_ADMIN, ROLE_API_EVENTS_DELETE"/>
    <sec:intercept-url pattern="/api/events/*/acl/*/*" method="DELETE" access="ROLE_ADMIN, ROLE_API_EVENTS_ACL_DELETE"/>
    <sec:intercept-url pattern="/api/events/*/metadata" method="DELETE" access="ROLE_ADMIN, ROLE_API_EVENTS_METADATA_DELETE"/>
    <sec:intercept-url pattern="/api/events/*/metadata/*" method="DELETE" access="ROLE_ADMIN, ROLE_API_EVENTS_METADATA_DELETE"/>
    <sec:intercept-url pattern="/api/groups/*" method="DELETE" access="ROLE_ADMIN, ROLE_API_GROUPS_DELETE"/>
    <sec:intercept-url pattern="/api/groups/*/members/*" method="DELETE" access="ROLE_ADMIN, ROLE_API_GROUPS_EDIT"/>
    <sec:intercept-url pattern="/api/series/*" method="DELETE" access="ROLE_ADMIN, ROLE_API_SERIES_DELETE"/>
    <sec:intercept-url pattern="/api/series/*/metadata" method="DELETE" access="ROLE_ADMIN, ROLE_API_SERIES_METADATA_DELETE"/>
    <sec:intercept-url pattern="/api/series/*/metadata/*" method="DELETE" access="ROLE_ADMIN, ROLE_API_SERIES_METADATA_DELETE"/>
    <sec:intercept-url pattern="/api/workflows/*" method="DELETE" access="ROLE_ADMIN, ROLE_API_WORKFLOW_INSTANCE_DELETE"/>

    <sec:intercept-url pattern="/admin-ng/css/**" access="ROLE_ANONYMOUS" />
    <sec:intercept-url pattern="/admin-ng/lib/**" access="ROLE_ANONYMOUS" />
    <sec:intercept-url pattern="/admin-ng/img/**" access="ROLE_ANONYMOUS" />
    <sec:intercept-url pattern="/admin-ng/js/**" access="ROLE_ANONYMOUS" />
    <sec:intercept-url pattern="/admin-ng/modules/**" access="ROLE_ANONYMOUS" />
    <sec:intercept-url pattern="/admin-ng/shared/**" access="ROLE_ANONYMOUS" />
    <sec:intercept-url pattern="/admin-ng/app.js" access="ROLE_ANONYMOUS" />

    <!-- Enable anonymous access to the admin ui -->
    <sec:intercept-url pattern="/admin-ng/public/**" access="ROLE_ANONYMOUS" />

    <!-- Enable anonymous access to the /info/me.json resource -->
    <sec:intercept-url pattern="/info/me.json" method="GET" access="ROLE_ANONYMOUS" />
    <sec:intercept-url pattern="/i18n/languages.json" method="GET" access="ROLE_ANONYMOUS" />
    <sec:intercept-url pattern="/info/components.json" method="GET" access="ROLE_ANONYMOUS" />

    <!-- anonymous access to user interface configuration -->
    <sec:intercept-url pattern="/ui/config/**" access="ROLE_ANONYMOUS" />

    <!-- Paella player -->
    <sec:intercept-url pattern="/paella/ui/auth.html" access="ROLE_USER" />
    <sec:intercept-url pattern="/paella/ui/**" access="ROLE_ANONYMOUS" />

    <!-- Enable anonymous access to the engage player and the GET endpoints it requires -->
    <sec:intercept-url pattern="/engage/ui/**" access="ROLE_ANONYMOUS" />
    <sec:intercept-url pattern="/engage/**" access="ROLE_ANONYMOUS" />
    <sec:intercept-url pattern="/engage-player/**" method="GET" access="ROLE_ANONYMOUS" />
    <sec:intercept-url pattern="/search/**" method="GET" access="ROLE_ANONYMOUS" />
    <sec:intercept-url pattern="/usertracking/**" method="GET" access="ROLE_ANONYMOUS" />
    <sec:intercept-url pattern="/usertracking/**" method="PUT" access="ROLE_ANONYMOUS" />
    <sec:intercept-url pattern="/static/**" method="GET" access="ROLE_ANONYMOUS" />
    <sec:intercept-url pattern="/export/**" method="GET" access="ROLE_ANONYMOUS" />

    <!-- Enable anonymous access to the annotation and the series endpoints -->
    <sec:intercept-url pattern="/series/**" method="GET" access="ROLE_ANONYMOUS, ROLE_CAPTURE_AGENT" />
    <sec:intercept-url pattern="/annotation/**" method="GET" access="ROLE_ANONYMOUS" />
    <sec:intercept-url pattern="/annotation/**" method="PUT" access="ROLE_ANONYMOUS" />

    <!-- Enable anonymous access to the OAI-PMH repository              -->
    <!-- The OAI-PMH specification demands boths GET and POST requests  -->
    <!-- Please make sure that the path configured here matches         -->
    <!-- the path configured for the repository servlet.                -->
    <sec:intercept-url pattern="/oaipmh/**" method="GET" access="ROLE_ADMIN"/>
    <sec:intercept-url pattern="/oaipmh/**" method="POST" access="ROLE_ADMIN"/>

    <!-- Enable anonymous access to the rss and atom feeds -->
    <sec:intercept-url pattern="/feeds/**" method="GET" access="ROLE_ANONYMOUS" />

    <!-- Secure the system management URLs for admins only -->
    <sec:intercept-url pattern="/services/available.*" method="GET" access="ROLE_ADMIN, ROLE_CAPTURE_AGENT" />
    <sec:intercept-url pattern="/services/**" access="ROLE_ADMIN"/>
    <sec:intercept-url pattern="/signing/**" access="ROLE_ADMIN" />
    <sec:intercept-url pattern="/system/**" access="ROLE_ADMIN" />
    <sec:intercept-url pattern="/config/**" access="ROLE_ADMIN" />

    <!-- Enable capture agent updates and ingest -->
    <sec:intercept-url pattern="/capture-admin/**" access="ROLE_ADMIN, ROLE_CAPTURE_AGENT" />
    <sec:intercept-url pattern="/recordings/**" method="GET" access="ROLE_ADMIN, ROLE_CAPTURE_AGENT" />
    <sec:intercept-url pattern="/ingest/**" access="ROLE_ADMIN, ROLE_CAPTURE_AGENT" />

    <!-- Secure the user management URLs for admins only -->
    <sec:intercept-url pattern="/users/**" access="ROLE_ADMIN" />
    <sec:intercept-url pattern="/admin/users.html" access="ROLE_ADMIN" />

    <!-- Enable 2-legged OAuth access ("signed fetch") to the LTI launch servlet -->
    <sec:intercept-url pattern="/lti" access="ROLE_OAUTH_USER" />

    <!-- Enable access to the LTI tools -->
    <sec:intercept-url pattern="/ltitools/**" access="ROLE_OAUTH_USER" />

    <sec:intercept-url pattern="/transcripts/watson/results*" method="GET" access="ROLE_ANONYMOUS" />
    <sec:intercept-url pattern="/transcripts/watson/results*" method="POST" access="ROLE_ANONYMOUS" />

    <!-- Everything else is for the admin users -->
    <sec:intercept-url pattern="/admin-ng" method="GET" access="ROLE_ADMIN, ROLE_ADMIN_UI, ROLE_COURSE_ADMIN" />
    <sec:intercept-url pattern="/admin-ng/" method="GET" access="ROLE_ADMIN, ROLE_ADMIN_UI, ROLE_COURSE_ADMIN" />
    <sec:intercept-url pattern="/admin-ng/index.html" access="ROLE_ADMIN, ROLE_ADMIN_UI, ROLE_COURSE_ADMIN" />
    <sec:intercept-url pattern="/index.html" access="ROLE_ADMIN, ROLE_ADMIN_UI, ROLE_COURSE_ADMIN" />
    <sec:intercept-url pattern="/**" access="ROLE_ADMIN, ROLE_COURSE_ADMIN" />

    <!-- ############################# -->
    <!-- # LOGIN / LOGOUT MECHANISMS # -->
    <!-- ############################# -->

    <!-- Uncomment to enable x509 client certificates for identifying clients -->
    <!-- sec:x509 subject-principal-regex="CN=(.*?)," user-service-ref="userDetailsService" / -->

    <!-- Enable and configure the failure URL for form-based logins -->
    <sec:form-login authentication-failure-url="/admin-ng/login.html?error" authentication-success-handler-ref="authSuccessHandler" />

    <!-- Authentication filter chain -->
    <sec:custom-filter position="BASIC_AUTH_FILTER" ref="authenticationFilters" />

    <sec:custom-filter ref="asyncTimeoutRedirectFilter" after="EXCEPTION_TRANSLATION_FILTER"/>

    <!-- Opencast is shipping its own implementation of the anonymous filter -->
    <sec:custom-filter ref="anonymousFilter" position="ANONYMOUS_FILTER" />

    <!-- Shibboleth header authentication filter
    <sec:custom-filter ref="shibbolethHeaderFilter" position="PRE_AUTH_FILTER"/>
    -->

    <!-- Enables "remember me" functionality -->
    <sec:remember-me services-ref="rememberMeServices" />

    <!-- Set the request cache -->
    <sec:request-cache ref="requestCache" />

    <!-- If any URLs are to be exposed to anonymous users, the "sec:anonymous" filter must be present -->
    <sec:anonymous enabled="false" />

    <!-- Enables log out -->
    <sec:logout success-handler-ref="logoutSuccessHandler" />

    <!-- Shibboleth log out
         Please specifiy the URL to return to after logging out
    <sec:logout logout-success-url="/Shibboleth.sso/Logout?return=http://www.opencast.org" />
    -->

  </sec:http>

  <bean id="rememberMeServices" class="org.opencastproject.kernel.security.SystemTokenBasedRememberMeService">
    <property name="userDetailsService" ref="userDetailsService"/>
    <!-- All following settings are optional -->
    <property name="tokenValiditySeconds" value="1209600"/>
    <property name="cookieName" value="oc-remember-me"/>
    <!-- The following key will be augmented by system properties. Thus, leaving this untouched is okay -->
    <property name="key" value="opencast"/>
  </bean>

  <!-- ############################# -->
  <!-- # Authentication Filters    # -->
  <!-- ############################# -->

  <bean id="authenticationFilters" class="org.springframework.web.filter.CompositeFilter">
    <property name="filters">
      <list>
        <!-- Digest auth is used by capture agents and is used to enable transparent clustering of services -->
        <!-- ATTENTION! Do not deactivate the digest filter, otherwise the distributed setup would not work -->
        <ref bean="digestFilter" />

        <!-- Basic authentication  -->
        <ref bean="basicAuthenticationFilter" />

        <!-- 2-legged OAuth is used by trusted 3rd party applications, including LTI. -->
        <!-- Uncomment the line below to support LTI or other OAuth clients.          -->
        <!-- <ref bean="oauthProtectedResourceFilter" /> -->
      </list>
    </property>
  </bean>

  <!-- ########################################### -->
  <!-- # Custom ajax timeout Filter Definition   # -->
  <!-- ########################################### -->

  <bean id="asyncTimeoutRedirectFilter" class="org.opencastproject.kernel.security.AsyncTimeoutRedirectFilter" />

  <!-- ######################################## -->
  <!-- # Custom Anonymous Filter Definition   # -->
  <!-- ######################################## -->

  <bean id="anonymousFilter" class="org.opencastproject.kernel.security.TrustedAnonymousAuthenticationFilter">
    <property name="userAttribute" ref="anonymousUserAttributes" />
    <property name="key" value="anonymousKey" />
  </bean>

  <bean id="anonymousUserAttributes" class="org.springframework.security.core.userdetails.memory.UserAttribute">
    <property name="authoritiesAsString" value="ROLE_ANONYMOUS"/>
    <property name="password" value="empty"/>
  </bean>

  <!-- ######################################## -->
  <!-- # Authentication Entry and Exit Points # -->
  <!-- ######################################## -->

  <!-- Differentiates between "normal" user requests and those requesting digest auth -->
  <bean id="opencastEntryPoint" class="org.opencastproject.kernel.security.DelegatingAuthenticationEntryPoint">
    <property name="userEntryPoint" ref="userEntryPoint" />
    <property name="digestAuthenticationEntryPoint" ref="digestEntryPoint" />
  </bean>

  <!-- Redirects unauthenticated requests to the login form -->
  <bean id="userEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
    <property name="loginFormUrl" value="/admin-ng/login.html" />
  </bean>

  <!-- Returns a 401 request for authentication via digest auth -->
  <bean id="digestEntryPoint" class="org.springframework.security.web.authentication.www.DigestAuthenticationEntryPoint">
    <property name="realmName" value="Opencast" />
    <property name="key" value="opencast" />
    <property name="nonceValiditySeconds" value="300" />
  </bean>

  <bean id="basicEntryPoint" class="org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint">
    <property name="realmName" value="Opencast"/>
  </bean>

  <bean id="authSuccessHandler" class="org.opencastproject.kernel.security.AuthenticationSuccessHandler">
    <property name="securityService" ref="securityService" />
    <property name="welcomePages">
      <map>
        <entry key="ROLE_ADMIN" value="/admin-ng/index.html" />
        <entry key="ROLE_ADMIN_UI" value="/admin-ng/index.html" />
        <entry key="*" value="/engage/ui/index.html" /> <!-- Any role not listed explicitly will redirect here -->
      </map>
    </property>
  </bean>

  <bean id="logoutSuccessHandler" class="org.opencastproject.kernel.security.LogoutSuccessHandler">
    <property name="userDirectoryService" ref="userDirectoryService" />
  </bean>

  <!-- ################# -->
  <!-- # Digest Filter # -->
  <!-- ################# -->

  <!-- Handles the details of the digest authentication dance -->
  <bean id="digestFilter" class="org.springframework.security.web.authentication.www.DigestAuthenticationFilter">
    <!--  Use only the in-memory users, as these have passwords that are not hashed -->
    <property name="userDetailsService" ref="userDetailsService" />
    <property name="authenticationEntryPoint" ref="digestEntryPoint" />
    <property name="createAuthenticatedToken" value="true" />
    <property name="userCache">
      <bean class="org.springframework.security.core.userdetails.cache.NullUserCache" />
    </property>
  </bean>

  <!-- ############################### -->
  <!-- # Basic Authentication Filter # -->
  <!-- ############################### -->

  <!-- Handles the details of the basic authentication dance -->
  <bean id="basicAuthenticationFilter" class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter">
    <property name="authenticationManager" ref="authenticationManager"/>
    <property name="authenticationEntryPoint" ref="basicEntryPoint"/>
  </bean>

  <!-- ####################### -->
  <!-- # OAuth (LTI) Support # -->
  <!-- ####################### -->

  <bean name="oauthProtectedResourceFilter" class="org.opencastproject.kernel.security.LtiProcessingFilter">
    <property name="consumerDetailsService" ref="oAuthConsumerDetailsService" />
    <property name="tokenServices">
      <bean class="org.springframework.security.oauth.provider.token.InMemoryProviderTokenServices" />
    </property>
    <property name="nonceServices">
      <bean class="org.springframework.security.oauth.provider.nonce.InMemoryNonceServices" />
    </property>
    <property name="authHandler" ref="ltiLaunchAuthenticationHandler" />
  </bean>

  <!-- ###################### -->
  <!-- # Shibboleth Support # -->
  <!-- ###################### -->

  <!-- General Shibboleth header extration filter
  <bean id="shibbolethHeaderFilter"
        class="org.opencastproject.security.shibboleth.ShibbolethRequestHeaderAuthenticationFilter">
    <property name="principalRequestHeader" value="<this need to be configured>"/>
    <property name="authenticationManager" ref="authenticationManager" />
    <property name="userDetailsService" ref="userDetailsService" />
    <property name="userDirectoryService" ref="userDirectoryService" />
    <property name="shibbolethLoginHandler" ref="aaiLoginHandler" />
    <property name="exceptionIfHeaderMissing" value="false" />
  </bean>
  -->

  <!-- AAI header extractor and user generator
  <bean id="aaiLoginHandler" class="org.opencastproject.security.aai.ConfigurableLoginHandler">
    <property name="securityService" ref="securityService" />
    <property name="userReferenceProvider" ref="userReferenceProvider" />
  </bean>
  -->

  <!--
  <bean id="preauthAuthProvider"
        class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
    <property name="preAuthenticatedUserDetailsService">
      <bean id="userDetailsServiceWrapper" class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
        <property name="userDetailsService" ref="userDetailsService"/>
      </bean>
    </property>
  </bean>
  -->

  <!-- #################### -->
  <!-- # OSGI Integration # -->
  <!-- #################### -->

  <!-- Obtain services from the OSGI service registry -->
  <osgi:reference id="userDetailsService" cardinality="1..1"
                  interface="org.springframework.security.core.userdetails.UserDetailsService" />

  <osgi:reference id="securityService" cardinality="1..1"
                  interface="org.opencastproject.security.api.SecurityService" />

  <!-- Uncomment to enable external users e.g. used together shibboleth -->
  <!-- <osgi:reference id="userReferenceProvider" cardinality="1..1"
                  interface="org.opencastproject.userdirectory.api.UserReferenceProvider"  /> -->

  <osgi:reference id="userDirectoryService" cardinality="1..1"
                  interface="org.opencastproject.security.api.UserDirectoryService" />

  <osgi:reference id="oAuthConsumerDetailsService" cardinality="1..1"
                  interface="org.springframework.security.oauth.provider.ConsumerDetailsService" />

  <osgi:reference id="ltiLaunchAuthenticationHandler" cardinality="1..1"
                  interface="org.springframework.security.oauth.provider.OAuthAuthenticationHandler" />

  <!-- ############################# -->
  <!-- # Spring Security Internals # -->
  <!-- ############################# -->

  <!-- The JPA user directory stores md5 hashed, salted passwords, so we must use a username-salted md5 password encoder. -->
  <sec:authentication-manager alias="authenticationManager">
    <!-- Uncomment this if using Shibboleth authentication -->
    <!--sec:authentication-provider ref="preauthAuthProvider" /-->
    <sec:authentication-provider user-service-ref="userDetailsService">
      <sec:password-encoder hash="md5"><sec:salt-source user-property="username" /></sec:password-encoder>
    </sec:authentication-provider>
  </sec:authentication-manager>

  <!-- Do not use a request cache -->
  <bean id="requestCache" class="org.springframework.security.web.savedrequest.NullRequestCache" />

</beans>

Output:
【漏洞类型】权限管理漏洞
【严重性】Medium
【漏洞摘要】在原始代码中，`ROLE_COURSE_ADMIN`被错误地配置为允许访问所有`/admin-ng`路径，这意味着拥有此角色的用户可以进行用户创建操作，而不需要`ROLE_ADMIN`权限。这违反了预期的安全策略，因为`ROLE_COURSE_ADMIN`暗示它是一个特定课程的管理员，不应该拥有创建新用户的权限。根本原因在于安全配置文件中错误地包括了`ROLE_COURSE_ADMIN`在所有`/admin-ng`路径的访问控制列表中。
【关键代码模式】`<sec:intercept-url pattern="/**" access="ROLE_ADMIN, ROLE_COURSE_ADMIN" />` 和 `<sec:intercept-url pattern="/admin-ng/**" access="ROLE_ADMIN, ROLE_COURSE_ADMIN, ROLE_ADMIN_UI" />`
【修复方法说明】通过移除`ROLE_COURSE_ADMIN`，修复了权限管理漏洞，确保只有具有`ROLE_ADMIN`权限的用户才能进行用户创建操作。


Instruction:
作为 Java 安全专家，检测此代码中的安全漏洞：
## 内容1
### 文件路径：
gradle.properties
### 代码内容：
#################################################
# Platform metadata for releases, POM generation, etc.
#################################################
group=org.apereo.cas
version=6.0.9-SNAPSHOT

projectUrl=http://www.apereo.org/cas
projectInceptionYear=2004

projectScmUrl=scm:git@github.com:apereo/cas.git

projectLicenseName=The Apache Software License, Version 2.0
projectLicenseUrl=http://www.apache.org/licenses/LICENSE-2.0.txt
projectLicenseDistribution=repo

projectIssueSystem=GithHub
projectIssueUrl=https://github.com/apereo/cas/issues

sonatypeRepositoryUrl=https://oss.sonatype.org/service/local/staging/deploy/maven2/
sonatypeSnapshotsRepositoryUrl=https://oss.sonatype.org/content/repositories/snapshots/
sonatypeReleasesRepositoryUrl=https://oss.sonatype.org/content/repositories/releases

###############################################################################################
# Localized gradle settings to ensure modules can be loaded/configured as quickly as possible.
################################################################################################
gradleVersion=5.6.3
org.gradle.configureondemand=true
org.gradle.jvmargs=-Xms1024m -Xmx4048m -XX:SoftRefLRUPolicyMSPerMB=0 -noverify -XX:TieredStopAtLevel=1

#################################################
# Platform SDK versions for both Java, etc.
#################################################
sourceCompatibility=11
targetCompatibility=11
lombokVersion=1.18.4

#################################################################################
# A number of dependency versions are defined in gradle/overrides.gradle file
# in order to fully override component versions that are provided by Spring Boot.
##################################################################################

aspectjVersion=1.9.2
errorProneVersion=2.3.2
errorproneJavacVersion=9+181-r4173-1
junitVersion=5.3.2
junitPlatformVersion=1.3.2
mockitoVersion=2.23.4
objenesisVersion=2.6

javaxSoapApiVersion=1.4.0
javaxJwsVersion=3.1.2.2
javaxJmsVersion=3.1.2.2
jaxbApiVersion=2.3.0
jaxbGlassfishVersion=2.3.0.1

#####################################################
# Gradle Plugins & Build Utilities
######################################################
spotbugsVersion=3.1.10
findbugsContribVersion=7.4.2.sb
findbugsSecVersion=1.8.0
checkstyleVersion=8.15
gradleLintVersion=10.3.2
gradleLombokVersion=1.14
gradleSpotbugsVersion=1.6.8
gradleSonarqubeVersion=2.6.2
gradleDependencyVersionsVersion=0.20.0
gradleDependencyCheckVersion=4.0.1
gradlePropDepsVersion=0.0.10.RELEASE
gradleErrorProneVersion=0.5
gradleNodeVersion=1.2.0
gradleGitVersion=2.3.0
gradleJacocoVersion=0.8.2

javaxServletVersion=4.0.1
javaxValidationVersion=2.0.1.Final
javassistVersion=3.24.0-GA
javaxJstlVersion=1.2

semverVersion=2.2.0
oshiVersion=3.12.0

swaggerVersion=1.5.20
swaggerSpringFoxVersion=2.9.2

slf4jVersion=1.8.0-beta2
disruptorVersion=3.4.2
inspektrVersion=1.8.4.GA
sentryRavenVersion=8.0.3

###############################
# Spring versions
###############################

springBootVersion=2.1.2.RELEASE
springBootAdminVersion=2.1.2

springIntegrationVersion=5.1.2.RELEASE

springWebflowClientVersion=1.0.3
springWebflowVersion=2.5.1.RELEASE

springDataCommonsVersion=2.1.3.RELEASE
springDataMongoDbVersion=2.1.3.RELEASE

springSecurityVersion=5.1.2.RELEASE
springSecurityRsaVersion=1.0.8.RELEASE
springShellVersion=2.0.1.RELEASE

springCloudConfigVersion=2.1.0.RELEASE
springCloudContextVersion=2.1.0.RELEASE
springCloudCommonsVersion=2.1.0.RELEASE
springCloudBusVersion=2.1.0.RELEASE

springCloudVaultVersion=2.1.0.RELEASE
springCloudZookeeperVersion=2.1.0.RELEASE
springCloudSleuthVersion=2.1.0.RELEASE
springCloudEurekaVersion=2.1.0.RELEASE
springCloudConsulVersion=2.1.0.RELEASE

springWsVersion=3.0.6.RELEASE
########################################

kafkaSpringVersion=2.2.2.RELEASE
zookeeperVersion=3.4.12
curatorVersion=4.0.1
eurekaClientVersion=1.9.8
ribbonVersion=2.3.0

wss4jVersion=2.2.2
wsdl4jVersion=1.6.3
saajImplVersion=1.5.0
bucket4jVersion=4.2.1

activemqPoolVersion=5.15.8

retrofitVersion=2.5.0
influxDbVersion=2.14
okhttp3Version=3.12.0

hazelcastAwsVersion=2.3
hazelcastAzureVersion=1.2.1
jcloudsGoogleEngineVersion=1.9.3
hazelcastJCloudsVersion=3.7.2
hazelcastKubernetesVersion=1.3.1
hazelcastSwarmVersion=1.0-RC8
jcloudsVersion=2.1.1

apacheCxfVersion=3.2.7
apacheFedizVersion=1.4.5
apacheFortressVersion=2.0.3
apacheVelocityVersion=1.7
apacheVelocityEngineVersion=2.0
apacheSyncopeVersion=2.1.2

zuulVersion=2.1.3

hibernateValidatorVersion=6.0.13.Final
dom4jVersion=2.1.1
javaParserVersion=3.8.1

casClientVersion=3.5.1
casSecurityFilterVersion=2.0.10.4
snakeYamlVersion=1.23

mongoDriverVersion=3.9.1
jtaVersion=1.1

splunkLoggingVersion=1.6.2

###############################
# Apache Commons
###############################

commonsLangVersion=3.8.1
commonsLoggingVersion=1.2
commonsValidatorVersion=1.6
commonsJexlVersion=1.1
commonsCodecVersion=1.11
commonsIoVersion=2.6
commonsTextVersion=1.6
commonsCliVersion=1.4
commonsPoolVersion=2.6.0
commonsBeansVersion=1.9.3
commonsCollections3Version=3.2.2
commonsCollectionsVersion=4.2

joolVersion=0.9.14
jasyptVersion=1.9.2

jodaTimeVersion=2.10.1
reflectionsVersion=0.9.11
guavaVersion=27.0.1-jre

pac4jSpringWebmvcVersion=3.2.0
pac4jVersion=3.6.1

statsdVersion=3.1.0
amazonSdkVersion=1.11.475
micrometerVersion=1.1.1

springDataCosmosDbVersion=2.0.3
cosmosdbVersion=2.1.0

azureKeyVaultSecretsVersion=2.0.7

jsonVersion=20160810
hjsonVersion=3.0.0
personDirectoryVersion=1.8.9
quartzVersion=2.3.0

okioHttpVersion=2.7.5
okioVersion=2.1.0

yubicoVersion=3.0.2
yubicoU2fServerVersion=0.19.1
duoVersion=1.1
authyVersion=1.5.0
googleAuthVersion=1.2.0
googleZxingVersion=3.3.3
maxmindVersion=2.12.0
googleMapsGeoCodingVersion=0.9.1
userInfoVersion=1.1.0

ldaptiveVersion=1.2.4
unboundidVersion=4.0.9

jcifsVersion=1.3.17
jcifsExtVersion=0.9.4

openidVersion=1.0.0
unboundidScim2Version=2.2.0
unboundidScim1Version=1.8.20
jerseyVersion=2.27

jradiusVersion=jradius-1.1.5

httpclientVersion=4.5.6

spymemcachedVersion=2.12.3
awsmemcachedVersion=1.1.1
kryoVersion=4.0.2
kryoSerializersVersion=0.42

ehcacheVersion=2.10.6
jcacheVersion=1.0.1
twilioVersion=7.33.0
nexmoVersion=4.0.1
textMagicVersion=1.2.0

shiroVersion=1.4.0
jose4jVersion=0.6.4

nimbusJoseVersion=6.5.1
nimbusOidcVersion=6.2
jsonSmartVersion=2.3

###############################
# SAML versions
###############################
opensamlVersion=3.4.0
xercesVersion=2.12.0
idpVersion=3.4.2
idpLibertyIdwsConsumerVersion=1.0.0
xmlapisVersion=1.4.01
bouncyCastleVersion=1.60
shibbolethUtilJavaSupportVersion=7.4.1
jdomVersion=1.1

cryptacularVersion=1.2.2

grouperVersion=2.4.0

jsonassertVersion=1.5.0

cassandraDriverVersion=3.6.0

igniteVersion=2.7.0

embeddedRedisVersion=0.6

nodeVersion=8.12.0

ektorpVersion=1.5.0

###############################
# Web JARs
###############################
headjsVersion=1.0.3
fontAwesomeVersion=5.6.1
jqueryUiVersion=1.12.1
jqueryVersion=3.3.1-1
zxcvbnVersion=4.3.0
bootstrapVersion=4.1.3
jqueryCookieVersion=1.4.1-1
datatablesVersion=1.10.19
bootstrapSelectVersion=1.12.4

Output:
【漏洞类型】版本管理漏洞
【严重性】Medium
【漏洞摘要】此代码中，`cryptacularVersion` 未更新到最新版本，存在安全风险。具体表现为，`cryptacularVersion` 从 1.2.2 更新到 1.2.4，修复了已知的安全漏洞。
【关键代码模式】版本定义，特别是不常用的库版本，需要定期更新以修复已知的安全漏洞。
【修复方法说明】将 `cryptacularVersion` 更新到最新版本 1.2.4，解决了已知的安全漏洞。


Instruction:
作为 Java 安全专家，检测此代码中的安全漏洞：
## 内容1
### 文件路径：
src/main/java/alfio/util/MustacheCustomTag.java
### 代码内容：
/**
 * This file is part of alf.io.
 *
 * alf.io is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * alf.io is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with alf.io.  If not, see <http://www.gnu.org/licenses/>.
 */
package alfio.util;

import alfio.controller.api.support.TicketHelper;
import com.samskivert.mustache.Mustache;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.text.StringEscapeUtils;
import org.commonmark.Extension;
import org.commonmark.ext.gfm.tables.TablesExtension;
import org.commonmark.node.Link;
import org.commonmark.node.Node;
import org.commonmark.node.Text;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.AttributeProvider;
import org.commonmark.renderer.html.HtmlRenderer;
import org.commonmark.renderer.text.TextContentRenderer;
import org.springframework.security.web.util.UrlUtils;

import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;

import static org.apache.commons.lang3.StringUtils.substring;

/**
 * For formatting date in a mustache template.
 *
 *

 * Syntax is: {{#format-date}}{{yourDate}} FORMAT locale:YOUR_LOCALE{{/format-date}}.
 *

 *

 * Where
 *

 *

 *
yourDate has been formatted following the java.time.ZonedDateTime
 *
FORMAT is a format understood by {@link DateTimeFormatter}

 *
optional: locale:YOUR_LOCALE you can define the locale

 *

 */
@Log4j2
public class MustacheCustomTag {

    private MustacheCustomTag() {}

    private static final Pattern ARG_PATTERN = Pattern.compile("\\[(.*?)]");
    private static final String LOCALE_LABEL = "locale:";

    static final Mustache.Lambda FORMAT_DATE = (frag, out) -> {
        String execution = frag.execute().trim();
        ZonedDateTime d = ZonedDateTime.parse(substring(execution, 0, execution.indexOf(' ')));
        Pair<String, Optional<Locale>> p = parseParams(execution);
        if (p.getRight().isPresent()) {
            out.write(DateTimeFormatter.ofPattern(p.getLeft(), p.getRight().get()).format(d));
        } else {
            out.write(DateTimeFormatter.ofPattern(p.getLeft()).format(d));
        }
    };

    /**
     * {{#render-markdown}}[markdown][.html|.text]{{/render-markdown}}
     * The string must end with either .html or .text, otherwise Markdown won't be parsed
     * e.g.
     * {{#render-markdown}}(link)[description].html{{/render-markdown}} will produce HTML output
     * {{#render-markdown}}(link)[description].text{{/render-markdown}} will produce text/plain output
     */
    static final Mustache.Lambda RENDER_MARKDOWN = (frag, out) -> {
        String execution = frag.execute().strip();
        if(execution.endsWith(".html")) {
            out.write(renderToHtmlCommonmarkEscaped(StringUtils.removeEnd(execution, ".html")));
        } else if(execution.endsWith(".text")) {
            out.write(renderToTextCommonmark(StringUtils.removeEnd(execution, ".text")));
        } else {
            out.write(execution);
        }
    };

    static final Mustache.Lambda COUNTRY_NAME = (frag, out) -> {
        String execution = frag.execute().trim();
        String code = substring(execution, 0, 2);
        Pair<String, Optional<Locale>> p = parseParams(execution);
        out.write(translateCountryCode(code, p.getRight().orElse(null)));
    };

    static String translateCountryCode(String code, Locale locale) {
        Locale lang = locale != null ? locale : Locale.ENGLISH;
        return Stream.concat(TicketHelper.getLocalizedCountries(lang).stream(), TicketHelper.getLocalizedCountriesForVat(lang).stream())
            .filter(p -> p.getKey().equalsIgnoreCase(code))
            .map(Pair::getValue)
            .findFirst()
            .orElse(code);
    }

    /**
     * {{#additional-field-value}}[Prefix][name][suffix]{{/additional-field-value}}
     * prefix is optional, unless a suffix is needed.
     */
    static final Function<Object, Mustache.Lambda> ADDITIONAL_FIELD_VALUE = obj -> (frag, out) -> {
        if( !(obj instanceof Map) || ((Map<?,?>)obj).isEmpty()) {
            log.warn("map not found or empty. Skipping additionalFieldValue tag");
            return;
        }
        Map<?, ?> fieldNamesAndValues = (Map<?, ?>) obj;
        String execution = frag.execute().trim();
        Matcher matcher = ARG_PATTERN.matcher(execution);
        List<String> args = new ArrayList<>();
        while(matcher.find()) {
            args.add(matcher.group(1));
        }
        if(args.isEmpty()) {
            return;
        }
        String name = args.get(args.size() > 1 ? 1 : 0);
        String prefix = args.size() > 1 ? args.get(0) + " " : "";
        String suffix = args.size() > 2 ? " "+args.get(2) : "";

        if(fieldNamesAndValues.containsKey(name)) {
            out.write(prefix + fieldNamesAndValues.get(name) + suffix);
        }
    };

    private static Pair<String, Optional<Locale>> parseParams(String r) {

        int indexLocale = r.indexOf(LOCALE_LABEL);
        int end = Math.min(r.length(), indexLocale != -1 ? indexLocale : r.length());
        String format = substring(r, r.indexOf(' '), end);

        //
        String[] res = r.split("\\s+");
        Optional<Locale> locale = Arrays.stream(res).filter(s -> s.startsWith(LOCALE_LABEL)).findFirst()
                .map(l -> LocaleUtil.forLanguageTag(substring(l, LOCALE_LABEL.length())));
        //

        return Pair.of(format, locale);
    }

    private static final List<Extension> COMMONMARK_EXTENSIONS = List.of(TablesExtension.create());
    private static final Parser COMMONMARK_PARSER = Parser.builder().extensions(COMMONMARK_EXTENSIONS).build();
    private static final HtmlRenderer COMMONMARK_RENDERER = HtmlRenderer.builder().extensions(COMMONMARK_EXTENSIONS).attributeProviderFactory((ctx) -> new TargetBlankProvider()).build();
    private static final TextContentRenderer COMMONMARK_TEXT_RENDERER = TextContentRenderer.builder().extensions(COMMONMARK_EXTENSIONS).build();
    private static final ThreadLocal<String> A11Y_NEW_TAB_LABEL = new ThreadLocal<>();

    //Open in a new window if the link contains an absolute url
    private static class TargetBlankProvider implements AttributeProvider {
        @Override
        public void setAttributes(Node node, String tagName, Map<String, String> attributes) {
            if (node instanceof Link) {
                Link l = (Link) node;
                String destination = StringUtils.trimToEmpty(l.getDestination());
                if (UrlUtils.isAbsoluteUrl(destination)) {
                    attributes.put("target", "_blank");
                    attributes.put("rel", "nofollow noopener noreferrer");
                    var newTabLabel = A11Y_NEW_TAB_LABEL.get();
                    if (newTabLabel != null) {
                        attributes.put("aria-label", ((Text)node.getFirstChild()).getLiteral() + " " + newTabLabel);
                    }
                }
            }
        }
    }
    public static String renderToHtmlCommonmarkEscaped(String input) {
        return renderToHtmlCommonmarkEscaped(input, null);
    }

    public static String renderToHtmlCommonmarkEscaped(String input, String localizedNewWindowLabel) {
        try {
            A11Y_NEW_TAB_LABEL.set(localizedNewWindowLabel);
            Node document = COMMONMARK_PARSER.parse(StringEscapeUtils.escapeHtml4(input));
            return COMMONMARK_RENDERER.render(document);
        } finally {
            A11Y_NEW_TAB_LABEL.remove();
        }
    }

    public static String renderToTextCommonmark(String input) {
        Node document = COMMONMARK_PARSER.parse(input);
        return COMMONMARK_TEXT_RENDERER.render(document);
    }
}


Instruction:
作为 Java 安全专家，检测此代码中的安全漏洞：
## 内容1
### 文件路径：
codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java
### 代码内容：
package io.netty.handler.codec;

import io.netty.util.HashingStrategy;

import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Set;

import static io.netty.util.HashingStrategy.JAVA_HASHER;
import static io.netty.util.internal.MathUtil.findNextPositivePowerOfTwo;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
import static java.lang.Math.max;
import static java.lang.Math.min;

/**
 * Default implementation of {@link Headers};
 *
 * @param <K> the type of the header name.
 * @param <V> the type of the header value.
 * @param <T> the type to use for return values when the intention is to return {@code this} object.
 */
public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers<K, V, T> {
    /**
     * Constant used to seed the hash code generation. Could be anything but this was borrowed from murmur3.
     */
    static final int HASH_CODE_SEED = 0xc2b2ae35;

    private final HeaderEntry<K, V>[] entries;
    protected final HeaderEntry<K, V> head;

    private final byte hashMask;
    private final ValueConverter<V> valueConverter;
    private final NameValidator<K> nameValidator;
    private final ValueValidator<V> valueValidator;
    private final HashingStrategy<K> hashingStrategy;
    int size;

    public interface NameValidator<K> {
        /**
         * Verify that {@code name} is valid.
         * @param name The name to validate.
         * @throws RuntimeException if {@code name} is not valid.
         */
        void validateName(K name);

        @SuppressWarnings("rawtypes")
        NameValidator NOT_NULL = new NameValidator() {
            @Override
            public void validateName(Object name) {
                checkNotNull(name, "name");
            }
        };
    }

    public interface ValueValidator<V> {
        /**
         * Validate the given value. If the validation fails, then an implementation specific runtime exception may be
         * thrown.
         *
         * @param value The value to validate.
         */
        void validate(V value);

        ValueValidator<?> NO_VALIDATION = new ValueValidator<Object>() {
            @Override
            public void validate(Object value) {
            }
        };
    }

    @SuppressWarnings("unchecked")
    public DefaultHeaders(ValueConverter<V> valueConverter) {
        this(JAVA_HASHER, valueConverter);
    }

    @SuppressWarnings("unchecked")
    public DefaultHeaders(ValueConverter<V> valueConverter, NameValidator<K> nameValidator) {
        this(JAVA_HASHER, valueConverter, nameValidator);
    }

    @SuppressWarnings("unchecked")
    public DefaultHeaders(HashingStrategy<K> nameHashingStrategy, ValueConverter<V> valueConverter) {
        this(nameHashingStrategy, valueConverter, NameValidator.NOT_NULL);
    }

    public DefaultHeaders(HashingStrategy<K> nameHashingStrategy,
            ValueConverter<V> valueConverter, NameValidator<K> nameValidator) {
        this(nameHashingStrategy, valueConverter, nameValidator, 16);
    }

    /**
     * Create a new instance.
     * @param nameHashingStrategy Used to hash and equality compare names.
     * @param valueConverter Used to convert values to/from native types.
     * @param nameValidator Used to validate name elements.
     * @param arraySizeHint A hint as to how large the hash data structure should be.
     * The next positive power of two will be used. An upper bound may be enforced.
     */
    @SuppressWarnings("unchecked")
    public DefaultHeaders(HashingStrategy<K> nameHashingStrategy,
                          ValueConverter<V> valueConverter, NameValidator<K> nameValidator, int arraySizeHint) {
        this(nameHashingStrategy, valueConverter, nameValidator, arraySizeHint,
                (ValueValidator<V>) ValueValidator.NO_VALIDATION);
    }

    /**
     * Create a new instance.
     * @param nameHashingStrategy Used to hash and equality compare names.
     * @param valueConverter Used to convert values to/from native types.
     * @param nameValidator Used to validate name elements.
     * @param arraySizeHint A hint as to how large the hash data structure should be.
     * The next positive power of two will be used. An upper bound may be enforced.
     * @param valueValidator The validation strategy for entry values.
     */
    @SuppressWarnings("unchecked")
    public DefaultHeaders(HashingStrategy<K> nameHashingStrategy, ValueConverter<V> valueConverter,
                          NameValidator<K> nameValidator, int arraySizeHint, ValueValidator<V> valueValidator) {
        this.valueConverter = checkNotNull(valueConverter, "valueConverter");
        this.nameValidator = checkNotNull(nameValidator, "nameValidator");
        hashingStrategy = checkNotNull(nameHashingStrategy, "nameHashingStrategy");
        this.valueValidator = checkNotNull(valueValidator, "valueValidator");
        // Enforce a bound of [2, 128] because hashMask is a byte. The max possible value of hashMask is one less
        // than the length of this array, and we want the mask to be > 0.
        entries = new HeaderEntry[findNextPositivePowerOfTwo(max(2, min(arraySizeHint, 128)))];
        hashMask = (byte) (entries.length - 1);
        head = new HeaderEntry<K, V>();
    }

    @Override
    public V get(K name) {
        checkNotNull(name, "name");

        int h = hashingStrategy.hashCode(name);
        int i = index(h);
        HeaderEntry<K, V> e = entries[i];
        V value = null;
        // loop until the first header was found
        while (e != null) {
            if (e.hash == h && hashingStrategy.equals(name, e.key)) {
                value = e.value;
            }

            e = e.next;
        }
        return value;
    }

    @Override
    public V get(K name, V defaultValue) {
        V value = get(name);
        if (value == null) {
            return defaultValue;
        }
        return value;
    }

    @Override
    public V getAndRemove(K name) {
        int h = hashingStrategy.hashCode(name);
        return remove0(h, index(h), checkNotNull(name, "name"));
    }

    @Override
    public V getAndRemove(K name, V defaultValue) {
        V value = getAndRemove(name);
        if (value == null) {
            return defaultValue;
        }
        return value;
    }

    @Override
    public List<V> getAll(K name) {
        checkNotNull(name, "name");

        LinkedList<V> values = new LinkedList<V>();

        int h = hashingStrategy.hashCode(name);
        int i = index(h);
        HeaderEntry<K, V> e = entries[i];
        while (e != null) {
            if (e.hash == h && hashingStrategy.equals(name, e.key)) {
                values.addFirst(e.getValue());
            }
            e = e.next;
        }
        return values;
    }

    /**
     * Equivalent to {@link #getAll(Object)} but no intermediate list is generated.
     * @param name the name of the header to retrieve
     * @return an {@link Iterator} of header values corresponding to {@code name}.
     */
    public Iterator<V> valueIterator(K name) {
        return new ValueIterator(name);
    }

    @Override
    public List<V> getAllAndRemove(K name) {
        List<V> all = getAll(name);
        remove(name);
        return all;
    }

    @Override
    public boolean contains(K name) {
        return get(name) != null;
    }

    @Override
    public boolean containsObject(K name, Object value) {
        return contains(name, fromObject(name, value));
    }

    @Override
    public boolean containsBoolean(K name, boolean value) {
        return contains(name, fromBoolean(name, value));
    }

    @Override
    public boolean containsByte(K name, byte value) {
        return contains(name, fromByte(name, value));
    }

    @Override
    public boolean containsChar(K name, char value) {
        return contains(name, fromChar(name, value));
    }

    @Override
    public boolean containsShort(K name, short value) {
        return contains(name, fromShort(name, value));
    }

    @Override
    public boolean containsInt(K name, int value) {
        return contains(name, fromInt(name, value));
    }

    @Override
    public boolean containsLong(K name, long value) {
        return contains(name, fromLong(name, value));
    }

    @Override
    public boolean containsFloat(K name, float value) {
        return contains(name, fromFloat(name, value));
    }

    @Override
    public boolean containsDouble(K name, double value) {
        return contains(name, fromDouble(name, value));
    }

    @Override
    public boolean containsTimeMillis(K name, long value) {
        return contains(name, fromTimeMillis(name, value));
    }

    @SuppressWarnings("unchecked")
    @Override
    public boolean contains(K name, V value) {
        return contains(name, value, JAVA_HASHER);
    }

    public final boolean contains(K name, V value, HashingStrategy<? super V> valueHashingStrategy) {
        checkNotNull(name, "name");

        int h = hashingStrategy.hashCode(name);
        int i = index(h);
        HeaderEntry<K, V> e = entries[i];
        while (e != null) {
            if (e.hash == h && hashingStrategy.equals(name, e.key) && valueHashingStrategy.equals(value, e.value)) {
                return true;
            }
            e = e.next;
        }
        return false;
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return head == head.after;
    }

    @Override
    public Set<K> names() {
        if (isEmpty()) {
            return Collections.emptySet();
        }
        Set<K> names = new LinkedHashSet<K>(size());
        HeaderEntry<K, V> e = head.after;
        while (e != head) {
            names.add(e.getKey());
            e = e.after;
        }
        return names;
    }

    @Override
    public T add(K name, V value) {
        validateName(nameValidator, true, name);
        validateValue(valueValidator, name, value);
        checkNotNull(value, "value");
        int h = hashingStrategy.hashCode(name);
        int i = index(h);
        add0(h, i, name, value);
        return thisT();
    }

    @Override
    public T add(K name, Iterable<? extends V> values) {
        validateName(nameValidator, true, name);
        int h = hashingStrategy.hashCode(name);
        int i = index(h);
        for (V v: values) {
            validateValue(valueValidator, name, v);
            add0(h, i, name, v);
        }
        return thisT();
    }

    @Override
    public T add(K name, V... values) {
        validateName(nameValidator, true, name);
        int h = hashingStrategy.hashCode(name);
        int i = index(h);
        for (V v: values) {
            validateValue(valueValidator, name, v);
            add0(h, i, name, v);
        }
        return thisT();
    }

    @Override
    public T addObject(K name, Object value) {
        return add(name, fromObject(name, value));
    }

    @Override
    public T addObject(K name, Iterable<?> values) {
        for (Object value : values) {
            addObject(name, value);
        }
        return thisT();
    }

    @Override
    public T addObject(K name, Object... values) {
        for (Object value: values) {
            addObject(name, value);
        }
        return thisT();
    }

    @Override
    public T addInt(K name, int value) {
        return add(name, fromInt(name, value));
    }

    @Override
    public T addLong(K name, long value) {
        return add(name, fromLong(name, value));
    }

    @Override
    public T addDouble(K name, double value) {
        return add(name, fromDouble(name, value));
    }

    @Override
    public T addTimeMillis(K name, long value) {
        return add(name, fromTimeMillis(name, value));
    }

    @Override
    public T addChar(K name, char value) {
        return add(name, fromChar(name, value));
    }

    @Override
    public T addBoolean(K name, boolean value) {
        return add(name, fromBoolean(name, value));
    }

    @Override
    public T addFloat(K name, float value) {
        return add(name, fromFloat(name, value));
    }

    @Override
    public T addByte(K name, byte value) {
        return add(name, fromByte(name, value));
    }

    @Override
    public T addShort(K name, short value) {
        return add(name, fromShort(name, value));
    }

    @Override
    public T add(Headers<? extends K, ? extends V, ?> headers) {
        if (headers == this) {
            throw new IllegalArgumentException("can't add to itself.");
        }
        addImpl(headers);
        return thisT();
    }

    protected void addImpl(Headers<? extends K, ? extends V, ?> headers) {
        if (headers instanceof DefaultHeaders) {
            @SuppressWarnings("unchecked")
            final DefaultHeaders<? extends K, ? extends V, T> defaultHeaders =
                    (DefaultHeaders<? extends K, ? extends V, T>) headers;
            HeaderEntry<? extends K, ? extends V> e = defaultHeaders.head.after;
            if (defaultHeaders.hashingStrategy == hashingStrategy &&
                    defaultHeaders.nameValidator == nameValidator) {
                // Fastest copy
                while (e != defaultHeaders.head) {
                    add0(e.hash, index(e.hash), e.key, e.value);
                    e = e.after;
                }
            } else {
                // Fast copy
                while (e != defaultHeaders.head) {
                    add(e.key, e.value);
                    e = e.after;
                }
            }
        } else {
            // Slow copy
            for (Entry<? extends K, ? extends V> header : headers) {
                add(header.getKey(), header.getValue());
            }
        }
    }

    @Override
    public T set(K name, V value) {
        validateName(nameValidator, false, name);
        validateValue(valueValidator, name, value);
        checkNotNull(value, "value");
        int h = hashingStrategy.hashCode(name);
        int i = index(h);
        remove0(h, i, name);
        add0(h, i, name, value);
        return thisT();
    }

    @Override
    public T set(K name, Iterable<? extends V> values) {
        validateName(nameValidator, false, name);
        checkNotNull(values, "values");

        int h = hashingStrategy.hashCode(name);
        int i = index(h);

        remove0(h, i, name);
        for (V v: values) {
            if (v == null) {
                break;
            }
            validateValue(valueValidator, name, v);
            add0(h, i, name, v);
        }

        return thisT();
    }

    @Override
    public T set(K name, V... values) {
        validateName(nameValidator, false, name);
        checkNotNull(values, "values");

        int h = hashingStrategy.hashCode(name);
        int i = index(h);

        remove0(h, i, name);
        for (V v: values) {
            if (v == null) {
                break;
            }
            validateValue(valueValidator, name, v);
            add0(h, i, name, v);
        }

        return thisT();
    }

    @Override
    public T setObject(K name, Object value) {
        V convertedValue = checkNotNull(fromObject(name, value), "convertedValue");
        return set(name, convertedValue);
    }

    @Override
    public T setObject(K name, Iterable<?> values) {
        validateName(nameValidator, false, name);

        int h = hashingStrategy.hashCode(name);
        int i = index(h);

        remove0(h, i, name);
        for (Object v: values) {
            if (v == null) {
                break;
            }
            add0(h, i, name, fromObject(name, v));
        }

        return thisT();
    }

    @Override
    public T setObject(K name, Object... values) {
        validateName(nameValidator, false, name);

        int h = hashingStrategy.hashCode(name);
        int i = index(h);

        remove0(h, i, name);
        for (Object v: values) {
            if (v == null) {
                break;
            }
            add0(h, i, name, fromObject(name, v));
        }

        return thisT();
    }

    @Override
    public T setInt(K name, int value) {
        return set(name, fromInt(name, value));
    }

    @Override
    public T setLong(K name, long value) {
        return set(name, fromLong(name, value));
    }

    @Override
    public T setDouble(K name, double value) {
        return set(name, fromDouble(name, value));
    }

    @Override
    public T setTimeMillis(K name, long value) {
        return set(name, fromTimeMillis(name, value));
    }

    @Override
    public T setFloat(K name, float value) {
        return set(name, fromFloat(name, value));
    }

    @Override
    public T setChar(K name, char value) {
        return set(name, fromChar(name, value));
    }

    @Override
    public T setBoolean(K name, boolean value) {
        return set(name, fromBoolean(name, value));
    }

    @Override
    public T setByte(K name, byte value) {
        return set(name, fromByte(name, value));
    }

    @Override
    public T setShort(K name, short value) {
        return set(name, fromShort(name, value));
    }

    @Override
    public T set(Headers<? extends K, ? extends V, ?> headers) {
        if (headers != this) {
            clear();
            addImpl(headers);
        }
        return thisT();
    }

    @Override
    public T setAll(Headers<? extends K, ? extends V, ?> headers) {
        if (headers != this) {
            for (K key : headers.names()) {
                remove(key);
            }
            addImpl(headers);
        }
        return thisT();
    }

    @Override
    public boolean remove(K name) {
        return getAndRemove(name) != null;
    }

    @Override
    public T clear() {
        Arrays.fill(entries, null);
        head.before = head.after = head;
        size = 0;
        return thisT();
    }

    @Override
    public Iterator<Entry<K, V>> iterator() {
        return new HeaderIterator();
    }

    @Override
    public Boolean getBoolean(K name) {
        V v = get(name);
        try {
            return v != null ? toBoolean(name, v) : null;
        } catch (RuntimeException ignore) {
            return null;
        }
    }

    @Override
    public boolean getBoolean(K name, boolean defaultValue) {
        Boolean v = getBoolean(name);
        return v != null ? v : defaultValue;
    }

    @Override
    public Byte getByte(K name) {
        V v = get(name);
        try {
            return v != null ? toByte(name, v) : null;
        } catch (RuntimeException ignore) {
            return null;
        }
    }

    @Override
    public byte getByte(K name, byte defaultValue) {
        Byte v = getByte(name);
        return v != null ? v : defaultValue;
    }

    @Override
    public Character getChar(K name) {
        V v = get(name);
        try {
            return v != null ? toChar(name, v) : null;
        } catch (RuntimeException ignore) {
            return null;
        }
    }

    @Override
    public char getChar(K name, char defaultValue) {
        Character v = getChar(name);
        return v != null ? v : defaultValue;
    }

    @Override
    public Short getShort(K name) {
        V v = get(name);
        try {
            return v != null ? toShort(name, v) : null;
        } catch (RuntimeException ignore) {
            return null;
        }
    }

    @Override
    public short getShort(K name, short defaultValue) {
        Short v = getShort(name);
        return v != null ? v : defaultValue;
    }

    @Override
    public Integer getInt(K name) {
        V v = get(name);
        try {
            return v != null ? toInt(name, v) : null;
        } catch (RuntimeException ignore) {
            return null;
        }
    }

    @Override
    public int getInt(K name, int defaultValue) {
        Integer v = getInt(name);
        return v != null ? v : defaultValue;
    }

    @Override
    public Long getLong(K name) {
        V v = get(name);
        try {
            return v != null ? toLong(name, v) : null;
        } catch (RuntimeException ignore) {
            return null;
        }
    }

    @Override
    public long getLong(K name, long defaultValue) {
        Long v = getLong(name);
        return v != null ? v : defaultValue;
    }

    @Override
    public Float getFloat(K name) {
        V v = get(name);
        try {
            return v != null ? toFloat(name, v) : null;
        } catch (RuntimeException ignore) {
            return null;
        }
    }

    @Override
    public float getFloat(K name, float defaultValue) {
        Float v = getFloat(name);
        return v != null ? v : defaultValue;
    }

    @Override
    public Double getDouble(K name) {
        V v = get(name);
        try {
            return v != null ? toDouble(name, v) : null;
        } catch (RuntimeException ignore) {
            return null;
        }
    }

    @Override
    public double getDouble(K name, double defaultValue) {
        Double v = getDouble(name);
        return v != null ? v : defaultValue;
    }

    @Override
    public Long getTimeMillis(K name) {
        V v = get(name);
        try {
            return v != null ? toTimeMillis(name, v) : null;
        } catch (RuntimeException ignore) {
            return null;
        }
    }

    @Override
    public long getTimeMillis(K name, long defaultValue) {
        Long v = getTimeMillis(name);
        return v != null ? v : defaultValue;
    }

    @Override
    public Boolean getBooleanAndRemove(K name) {
        V v = getAndRemove(name);
        try {
            return v != null ? toBoolean(name, v) : null;
        } catch (RuntimeException ignore) {
            return null;
        }
    }

    @Override
    public boolean getBooleanAndRemove(K name, boolean defaultValue) {
        Boolean v = getBooleanAndRemove(name);
        return v != null ? v : defaultValue;
    }

    @Override
    public Byte getByteAndRemove(K name) {
        V v = getAndRemove(name);
        try {
            return v != null ? toByte(name, v) : null;
        } catch (RuntimeException ignore) {
            return null;
        }
    }

    @Override
    public byte getByteAndRemove(K name, byte defaultValue) {
        Byte v = getByteAndRemove(name);
        return v != null ? v : defaultValue;
    }

    @Override
    public Character getCharAndRemove(K name) {
        V v = getAndRemove(name);
        try {
            return v != null ? toChar(name, v) : null;
        } catch (RuntimeException ignore) {
            return null;
        }
    }

    @Override
    public char getCharAndRemove(K name, char defaultValue) {
        Character v = getCharAndRemove(name);
        return v != null ? v : defaultValue;
    }

    @Override
    public Short getShortAndRemove(K name) {
        V v = getAndRemove(name);
        try {
            return v != null ? toShort(name, v) : null;
        } catch (RuntimeException ignore) {
            return null;
        }
    }

    @Override
    public short getShortAndRemove(K name, short defaultValue) {
        Short v = getShortAndRemove(name);
        return v != null ? v : defaultValue;
    }

    @Override
    public Integer getIntAndRemove(K name) {
        V v = getAndRemove(name);
        try {
            return v != null ? toInt(name, v) : null;
        } catch (RuntimeException ignore) {
            return null;
        }
    }

    @Override
    public int getIntAndRemove(K name, int defaultValue) {
        Integer v = getIntAndRemove(name);
        return v != null ? v : defaultValue;
    }

    @Override
    public Long getLongAndRemove(K name) {
        V v = getAndRemove(name);
        try {
            return v != null ? toLong(name, v) : null;
        } catch (RuntimeException ignore) {
            return null;
        }
    }

    @Override
    public long getLongAndRemove(K name, long defaultValue) {
        Long v = getLongAndRemove(name);
        return v != null ? v : defaultValue;
    }

    @Override
    public Float getFloatAndRemove(K name) {
        V v = getAndRemove(name);
        try {
            return v != null ? toFloat(name, v) : null;
        } catch (RuntimeException ignore) {
            return null;
        }
    }

    @Override
    public float getFloatAndRemove(K name, float defaultValue) {
        Float v = getFloatAndRemove(name);
        return v != null ? v : defaultValue;
    }

    @Override
    public Double getDoubleAndRemove(K name) {
        V v = getAndRemove(name);
        try {
            return v != null ? toDouble(name, v) : null;
        } catch (RuntimeException ignore) {
            return null;
        }
    }

    @Override
    public double getDoubleAndRemove(K name, double defaultValue) {
        Double v = getDoubleAndRemove(name);
        return v != null ? v : defaultValue;
    }

    @Override
    public Long getTimeMillisAndRemove(K name) {
        V v = getAndRemove(name);
        try {
            return v != null ? toTimeMillis(name, v) : null;
        } catch (RuntimeException ignore) {
            return null;
        }
    }

    @Override
    public long getTimeMillisAndRemove(K name, long defaultValue) {
        Long v = getTimeMillisAndRemove(name);
        return v != null ? v : defaultValue;
    }

    @SuppressWarnings("unchecked")
    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Headers)) {
            return false;
        }

        return equals((Headers<K, V, ?>) o, JAVA_HASHER);
    }

    @SuppressWarnings("unchecked")
    @Override
    public int hashCode() {
        return hashCode(JAVA_HASHER);
    }

    /**
     * Test this object for equality against {@code h2}.
     * @param h2 The object to check equality for.
     * @param valueHashingStrategy Defines how values will be compared for equality.
     * @return {@code true} if this object equals {@code h2} given {@code valueHashingStrategy}.
     * {@code false} otherwise.
     */
    public final boolean equals(Headers<K, V, ?> h2, HashingStrategy<V> valueHashingStrategy) {
        if (h2.size() != size()) {
            return false;
        }

        if (this == h2) {
            return true;
        }

        for (K name : names()) {
            List<V> otherValues = h2.getAll(name);
            List<V> values = getAll(name);
            if (otherValues.size() != values.size()) {
                return false;
            }
            for (int i = 0; i < otherValues.size(); i++) {
                if (!valueHashingStrategy.equals(otherValues.get(i), values.get(i))) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Generate a hash code for this object given a {@link HashingStrategy} to generate hash codes for
     * individual values.
     * @param valueHashingStrategy Defines how values will be hashed.
     */
    public final int hashCode(HashingStrategy<V> valueHashingStrategy) {
        int result = HASH_CODE_SEED;
        for (K name : names()) {
            result = 31 * result + hashingStrategy.hashCode(name);
            List<V> values = getAll(name);
            for (int i = 0; i < values.size(); ++i) {
                result = 31 * result + valueHashingStrategy.hashCode(values.get(i));
            }
        }
        return result;
    }

    @Override
    public String toString() {
        return HeadersUtils.toString(getClass(), iterator(), size());
    }

    /**
     * Call out to the given {@link NameValidator} to validate the given name.
     *
     * @param validator the validator to use
     * @param forAdd {@code true } if this validation is for adding to the headers, or {@code false} if this is for
     * setting (overwriting) the given header.
     * @param name the name to validate.
     */
    protected void validateName(NameValidator<K> validator, boolean forAdd, K name) {
        validator.validateName(name);
    }

    protected void validateValue(ValueValidator<V> validator, K name, V value) {
        validator.validate(value);
    }

    protected HeaderEntry<K, V> newHeaderEntry(int h, K name, V value, HeaderEntry<K, V> next) {
        return new HeaderEntry<K, V>(h, name, value, next, head);
    }

    protected ValueConverter<V> valueConverter() {
        return valueConverter;
    }

    protected NameValidator<K> nameValidator() {
        return nameValidator;
    }

    protected ValueValidator<V> valueValidator() {
        return valueValidator;
    }

    private int index(int hash) {
        return hash & hashMask;
    }

    private void add0(int h, int i, K name, V value) {
        // Update the hash table.
        entries[i] = newHeaderEntry(h, name, value, entries[i]);
        ++size;
    }

    /**
     * @return the first value inserted whose hash code equals {@code h} and whose name is equal to {@code name}.
     */
    private V remove0(int h, int i, K name) {
        HeaderEntry<K, V> e = entries[i];
        if (e == null) {
            return null;
        }

        V value = null;
        HeaderEntry<K, V> next = e.next;
        while (next != null) {
            if (next.hash == h && hashingStrategy.equals(name, next.key)) {
                value = next.value;
                e.next = next.next;
                next.remove();
                --size;
            } else {
                e = next;
            }

            next = e.next;
        }

        e = entries[i];
        if (e.hash == h && hashingStrategy.equals(name, e.key)) {
            if (value == null) {
                value = e.value;
            }
            entries[i] = e.next;
            e.remove();
            --size;
        }

        return value;
    }

    HeaderEntry<K, V> remove0(HeaderEntry<K, V> entry, HeaderEntry<K, V> previous) {
        int i = index(entry.hash);
        HeaderEntry<K, V> firstEntry = entries[i];
        if (firstEntry == entry) {
            entries[i] = entry.next;
            previous = entries[i];
        } else if (previous == null) {
            // If we don't have any existing starting point, then start from the beginning.
            previous = firstEntry;
            HeaderEntry<K, V> next = firstEntry.next;
            while (next != null && next != entry) {
                previous = next;
                next = next.next;
            }
            assert next != null: "Entry not found in its hash bucket: " + entry;
            previous.next = entry.next;
        } else {
            previous.next = entry.next;
        }
        entry.remove();
        --size;
        return previous;
    }

    @SuppressWarnings("unchecked")
    private T thisT() {
        return (T) this;
    }

    private V fromObject(K name, Object value) {
        try {
            return valueConverter.convertObject(checkNotNull(value, "value"));
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert object value for header '" + name + '\'', e);
        }
    }

    private V fromBoolean(K name, boolean value) {
        try {
            return valueConverter.convertBoolean(value);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert boolean value for header '" + name + '\'', e);
        }
    }

    private V fromByte(K name, byte value) {
        try {
            return valueConverter.convertByte(value);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert byte value for header '" + name + '\'', e);
        }
    }

    private V fromChar(K name, char value) {
        try {
            return valueConverter.convertChar(value);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert char value for header '" + name + '\'', e);
        }
    }

    private V fromShort(K name, short value) {
        try {
            return valueConverter.convertShort(value);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert short value for header '" + name + '\'', e);
        }
    }

    private V fromInt(K name, int value) {
        try {
            return valueConverter.convertInt(value);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert int value for header '" + name + '\'', e);
        }
    }

    private V fromLong(K name, long value) {
        try {
            return valueConverter.convertLong(value);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert long value for header '" + name + '\'', e);
        }
    }

    private V fromFloat(K name, float value) {
        try {
            return valueConverter.convertFloat(value);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert float value for header '" + name + '\'', e);
        }
    }

    private V fromDouble(K name, double value) {
        try {
            return valueConverter.convertDouble(value);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert double value for header '" + name + '\'', e);
        }
    }

    private V fromTimeMillis(K name, long value) {
        try {
            return valueConverter.convertTimeMillis(value);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert millsecond value for header '" + name + '\'', e);
        }
    }

    private boolean toBoolean(K name, V value) {
        try {
            return valueConverter.convertToBoolean(value);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert header value to boolean for header '" + name + '\'');
        }
    }

    private byte toByte(K name, V value) {
        try {
            return valueConverter.convertToByte(value);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert header value to byte for header '" + name + '\'');
        }
    }

    private char toChar(K name, V value) {
        try {
            return valueConverter.convertToChar(value);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert header value to char for header '" + name + '\'');
        }
    }

    private short toShort(K name, V value) {
        try {
            return valueConverter.convertToShort(value);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert header value to short for header '" + name + '\'');
        }
    }

    private int toInt(K name, V value) {
        try {
            return valueConverter.convertToInt(value);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert header value to int for header '" + name + '\'');
        }
    }

    private long toLong(K name, V value) {
        try {
            return valueConverter.convertToLong(value);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert header value to long for header '" + name + '\'');
        }
    }

    private float toFloat(K name, V value) {
        try {
            return valueConverter.convertToFloat(value);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert header value to float for header '" + name + '\'');
        }
    }

    private double toDouble(K name, V value) {
        try {
            return valueConverter.convertToDouble(value);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert header value to double for header '" + name + '\'');
        }
    }

    private long toTimeMillis(K name, V value) {
        try {
            return valueConverter.convertToTimeMillis(value);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException(
                    "Failed to convert header value to millsecond for header '" + name + '\'');
        }
    }

    /**
     * Returns a deep copy of this instance.
     */
    public DefaultHeaders<K, V, T> copy() {
        DefaultHeaders<K, V, T> copy = new DefaultHeaders<K, V, T>(
                hashingStrategy, valueConverter, nameValidator, entries.length);
        copy.addImpl(this);
        return copy;
    }

    private final class HeaderIterator implements Iterator<Entry<K, V>> {
        private HeaderEntry<K, V> current = head;

        @Override
        public boolean hasNext() {
            return current.after != head;
        }

        @Override
        public Entry<K, V> next() {
            current = current.after;

            if (current == head) {
                throw new NoSuchElementException();
            }

            return current;
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException("read only");
        }
    }

    private final class ValueIterator implements Iterator<V> {
        private final K name;
        private final int hash;
        private HeaderEntry<K, V> removalPrevious;
        private HeaderEntry<K, V> previous;
        private HeaderEntry<K, V> next;

        ValueIterator(K name) {
            this.name = checkNotNull(name, "name");
            hash = hashingStrategy.hashCode(name);
            calculateNext(entries[index(hash)]);
        }

        @Override
        public boolean hasNext() {
            return next != null;
        }

        @Override
        public V next() {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            if (previous != null) {
                removalPrevious = previous;
            }
            previous = next;
            calculateNext(next.next);
            return previous.value;
        }

        @Override
        public void remove() {
            if (previous == null) {
                throw new IllegalStateException();
            }
            removalPrevious = remove0(previous, removalPrevious);
            previous = null;
        }

        private void calculateNext(HeaderEntry<K, V> entry) {
            while (entry != null) {
                if (entry.hash == hash && hashingStrategy.equals(name, entry.key)) {
                    next = entry;
                    return;
                }
                entry = entry.next;
            }
            next = null;
        }
    }

    protected static class HeaderEntry<K, V> implements Entry<K, V> {
        protected final int hash;
        protected final K key;
        protected V value;
        /**
         * In bucket linked list
         */
        protected HeaderEntry<K, V> next;
        /**
         * Overall insertion order linked list
         */
        protected HeaderEntry<K, V> before, after;

        protected HeaderEntry(int hash, K key) {
            this.hash = hash;
            this.key = key;
        }

        HeaderEntry(int hash, K key, V value, HeaderEntry<K, V> next, HeaderEntry<K, V> head) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;

            after = head;
            before = head.before;
            pointNeighborsToThis();
        }

        HeaderEntry() {
            hash = -1;
            key = null;
            before = after = this;
        }

        protected final void pointNeighborsToThis() {
            before.after = this;
            after.before = this;
        }

        public final HeaderEntry<K, V> before() {
            return before;
        }

        public final HeaderEntry<K, V> after() {
            return after;
        }

        protected void remove() {
            before.after = after;
            after.before = before;
        }

        @Override
        public final K getKey() {
            return key;
        }

        @Override
        public final V getValue() {
            return value;
        }

        @Override
        public final V setValue(V value) {
            checkNotNull(value, "value");
            V oldValue = this.value;
            this.value = value;
            return oldValue;
        }

        @Override
        public final String toString() {
            return key.toString() + '=' + value.toString();
        }

        @Override
        public boolean equals(Object o) {
            if (!(o instanceof Map.Entry)) {
                return false;
            }
            Entry<?, ?> other = (Entry<?, ?>) o;
            return (getKey() == null ? other.getKey() == null : getKey().equals(other.getKey()))  &&
                   (getValue() == null ? other.getValue() == null : getValue().equals(other.getValue()));
        }

        @Override
        public int hashCode() {
            return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode());
        }
    }
}

Output:
【漏洞类型】安全逻辑漏洞
【严重性】Medium
【漏洞摘要】原始代码中的`setObject`方法，当传入数组或迭代器时，没有对转换后的值进行验证，导致可能绕过值验证。例如，在`setObject(K name, Iterable<?> values)`和`setObject(K name, Object... values)`方法中，`valueValidator.validate(value)`在调用`fromObject(name, v)`后被遗漏。
【关键代码模式】`setObject`方法，`validateValue`函数调用缺失，`fromObject`函数调用。
【修复方法说明】修复方法通过在调用`fromObject`后添加`validateValue`函数调用来确保值验证逻辑不会被绕过。这遵循了输入验证的最佳实践，即确保所有输入数据在使用前都经过适当的验证。