code: /**
   * This file is part of Eclipse Steady.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   *
   * SPDX-License-Identifier: Apache-2.0
   * SPDX-FileCopyrightText: Copyright (c) 2018-2020 SAP SE or an SAP affiliate company and Eclipse Steady contributors
   */
  package org.eclipse.steady.backend.rest;
  
  import static org.hamcrest.Matchers.is;
  import static org.junit.Assert.assertEquals;
  import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
  import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
  import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
  import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
  
  import java.nio.file.Paths;
  import java.util.Arrays;
  import java.util.HashSet;
  import java.util.Set;
  import java.util.function.Predicate;
  
  import javax.persistence.EntityNotFoundException;
  
  import org.eclipse.steady.backend.model.Application;
  import org.eclipse.steady.backend.model.Bug;
  import org.eclipse.steady.backend.model.Dependency;
  import org.eclipse.steady.backend.model.Library;
  import org.eclipse.steady.backend.model.Space;
  import org.eclipse.steady.backend.model.Tenant;
  import org.eclipse.steady.backend.repo.AffectedLibraryRepository;
  import org.eclipse.steady.backend.repo.ApplicationRepository;
  import org.eclipse.steady.backend.repo.BugRepository;
  import org.eclipse.steady.backend.repo.ConstructIdRepository;
  import org.eclipse.steady.backend.repo.GoalExecutionRepository;
  import org.eclipse.steady.backend.repo.LibraryRepository;
  import org.eclipse.steady.backend.repo.SpaceRepository;
  import org.eclipse.steady.backend.repo.TenantRepository;
  import org.eclipse.steady.shared.enums.ExportConfiguration;
  import org.eclipse.steady.shared.enums.Scope;
  import org.eclipse.steady.shared.json.JacksonUtil;
  import org.eclipse.steady.shared.util.FileUtil;
  import org.junit.After;
  import org.junit.Assert;
  import org.junit.Before;
  import org.junit.Test;
  import org.junit.runner.RunWith;
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.boot.test.context.SpringBootTest;
  import org.springframework.http.converter.HttpMessageConverter;
  import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
  import org.springframework.test.context.ActiveProfiles;
  import org.springframework.test.context.junit4.SpringRunner;
  import org.springframework.test.web.servlet.MockMvc;
  import org.springframework.test.web.servlet.MvcResult;
  import org.springframework.web.context.WebApplicationContext;
  
  @RunWith(SpringRunner.class)
  @SpringBootTest(classes = MainController.class, webEnvironment = SpringBootTest.WebEnvironment.MOCK)
  @ActiveProfiles("test")
  public class HubIntegrationControllerTest {
  
    private MockMvc mockMvc;
    private HttpMessageConverter<?> mappingJackson2HttpMessageConverter;
  
    public static final String DEFAULT_TENANT = "default";
    public static final String DEFAULT_SPACE = "public";
  
    public static final String SECOND_TENANT = "second";
    public static final String SECOND_SPACE = "second";
  
    private static final String APP_GROUP = "com.acme";
    private static final String APP_ARTIFACT = "vulas";
    private static int APP_VERSION = 1;
  
    @Autowired private ApplicationRepository appRepository;
  
    @Autowired private LibraryRepository libRepository;
  
    @Autowired private ConstructIdRepository cidRepository;
  
    @Autowired private GoalExecutionRepository gexeRepository;
  
    @Autowired private WebApplicationContext webApplicationContext;
  
    @Autowired private BugRepository bugRepository;
  
    @Autowired private TenantRepository tenantRepository;
  
    @Autowired private SpaceRepository spaceRepository;
  
    @Autowired private AffectedLibraryRepository affLibRepository;
  
    @Autowired
    void setConverters(HttpMessageConverter<?>[] converters) {
      this.mappingJackson2HttpMessageConverter =
          Arrays.asList(converters).stream()
              .filter(
                  new Predicate<HttpMessageConverter<?>>() {
                    @Override
                    public boolean test(HttpMessageConverter<?> hmc) {
                      return hmc instanceof MappingJackson2HttpMessageConverter;
                    }
                  })
              .findAny()
              .get();
      Assert.assertNotNull(
          "the JSON message converter must not be null", this.mappingJackson2HttpMessageConverter);
    }
  
    @Before
    public void setup() throws Exception {
      this.mockMvc = webAppContextSetup(webApplicationContext).build();
      this.gexeRepository.deleteAll();
      this.appRepository.deleteAll();
      this.libRepository.deleteAll();
      this.bugRepository.deleteAll();
      this.cidRepository.deleteAll();
  
      createTenantAndSpace(DEFAULT_TENANT, DEFAULT_SPACE, true);
      createTenantAndSpace(SECOND_TENANT, SECOND_SPACE, false);
    }
  
    @After
    public void reset() throws Exception {
      this.gexeRepository.deleteAll();
      this.appRepository.deleteAll();
      this.libRepository.deleteAll();
      this.bugRepository.deleteAll();
      this.cidRepository.deleteAll();
    }
  
    /**
     * Results in a bad request (400), because the requested space belongs to a wrong tenant.
     *
     * @throws Exception
     */
    @Test
    public void testBadSpaceExportRequest() throws Exception {
      final String item_id = SECOND_SPACE + "%20(" + SECOND_SPACE + ")";
      mockMvc
          .perform(get("/hubIntegration/apps/" + item_id + "/vulndeps"))
          .andExpect(status().is4xxClientError());
    }
  
    @Test
    public void testGetHubApps() throws Exception {
      // Rest-post http-client 4.1.3
      final Library lib =
          (Library)
              JacksonUtil.asObject(
                  FileUtil.readFile(
                      Paths.get("./src/test/resources/real_examples/lib_http-client-4.1.3.json")),
                  Library.class);
      this.libRepository.customSave(lib);
  
      // Rest-post bug
      final Bug bug =
          (Bug)
              JacksonUtil.asObject(
                  FileUtil.readFile(
                      Paths.get("./src/test/resources/real_examples/bug_2015-5262.json")),
                  Bug.class);
      this.bugRepository.customSave(bug, true);
  
      // Rest-post app using http-client
      final Application app = new Application(APP_GROUP, APP_ARTIFACT, "0.0." + APP_VERSION);
  
      // Dependencies
      final Set<Dependency> app_dependency = new HashSet<Dependency>();
      app_dependency.add(new Dependency(app, lib, Scope.COMPILE, false, "httpclient-4.1.3.jar"));
      String token = spaceRepository.getDefaultSpace(null).getSpaceToken();
      app.setSpace(spaceRepository.getDefaultSpace(null));
  
      app.setDependencies(app_dependency);
      this.appRepository.customSave(app);
  
      // Make sure the app exists
      assertEquals(1, this.appRepository.count());
  
      // Read all public apps as strings
      MvcResult response =
          mockMvc
              .perform(get("/hubIntegration/apps"))
              .andExpect(status().isOk())
              .andExpect(
                  content()
                      .string(
                          "[\""
                              + DEFAULT_SPACE
                              + " ("
                              + token
                              + ") "
                              + app.getMvnGroup()
                              + ":"
                              + app.getArtifact()
                              + ":"
                              + app.getVersion()
                              + "\"]"))
              .andReturn();
  
      // Read all public apps as JSON
      response =
          mockMvc
              .perform(get("/hubIntegration/apps/json"))
              .andExpect(status().isOk())
              .andExpect(jsonPath("$[0].application.group").value(app.getMvnGroup()))
              .andReturn();
    }
  
    @Test
    public void testGetHubAppVulnerabilities() throws Exception {
      final Library lib_foo =
          (Library)
              JacksonUtil.asObject(
                  FileUtil.readFile(
                      Paths.get(
                          "./src/test/resources/real_examples/lib_commons-fileupload-1.2.2.json")),
                  Library.class);
      this.libRepository.customSave(lib_foo);
  
      final Library lib_bar =
          (Library)
              JacksonUtil.asObject(
                  FileUtil.readFile(Paths.get("./src/test/resources/dummy_app/lib_bar.json")),
                  Library.class);
      this.libRepository.customSave(lib_bar);
  
      final Bug bug_foo =
          (Bug)
              JacksonUtil.asObject(
                  FileUtil.readFile(Paths.get("./src/test/resources/dummy_app/bug_foo.json")),
                  Bug.class);
      this.bugRepository.customSave(bug_foo, true);
  
      final Bug bug =
          (Bug)
              JacksonUtil.asObject(
                  FileUtil.readFile(
                      Paths.get("./src/test/resources/real_examples/bug_CVE-2014-0050.json")),
                  Bug.class);
      this.bugRepository.customSave(bug, true);
  
      // App with dependencies on foo and bar
      final Application app = new Application(APP_GROUP, APP_ARTIFACT, "0.0." + APP_VERSION);
      final Set<Dependency> app_dependency = new HashSet<Dependency>();
      app_dependency.add(new Dependency(app, lib_foo, Scope.COMPILE, false, "foo.jar"));
      app_dependency.add(new Dependency(app, lib_bar, Scope.COMPILE, false, "bar.jar"));
      app.setSpace(spaceRepository.getDefaultSpace(null));
      app.setDependencies(app_dependency);
      this.appRepository.customSave(app);
  
      // Construct item ID
      final String token = spaceRepository.getDefaultSpace(null).getSpaceToken();
      String item =
          DEFAULT_SPACE
              + " ("
              + token
              + ") "
              + app.getMvnGroup()
              + ":"
              + app.getArtifact()
              + ":"
              + app.getVersion();
  
      // Get vuln deps - default include all unassessed: IGN_UNASS_OFF
      MvcResult response =
          mockMvc
              .perform(get("/hubIntegration/apps/" + item + "/vulndeps"))
              .andExpect(status().isOk())
              .andExpect(jsonPath("$[0].spaceToken").exists())
              .andExpect(jsonPath("$[0].appId").exists())
              .andExpect(jsonPath("$[0].lastScan").exists())
              .andExpect(jsonPath("$[0].reachable").exists())
              .andReturn();
  
      // Only bar.jar is reported (unknown digest), foo.jar is ignored (well-known digest)
      Set<HubIntegrationController.VulnerableItemDependency> vuln_deps =
          (Set<HubIntegrationController.VulnerableItemDependency>)
              JacksonUtil.asObject(response.getResponse().getContentAsString(), Set.class);
      assertEquals(2, vuln_deps.size());
  
      // Get vuln deps - treatment of unassessed: IGN_UNASS_KNOWN
      response =
          mockMvc
              .perform(
                  get(
                      "/hubIntegration/apps/"
                          + item
                          + "/vulndeps?ignoreUnassessed="
                          + HubIntegrationController.IGN_UNASS_KNOWN))
              .andExpect(status().isOk())
              .andExpect(jsonPath("$[0].spaceToken").exists())
              .andExpect(jsonPath("$[0].appId").exists())
              .andExpect(jsonPath("$[0].lastScan").exists())
              .andExpect(
                  jsonPath("$[0].projectId")
                      .value(
                          "bar.jar")) // Only bar.jar is reported (unknown digest), foo.jar is ignored
              // (well-known digest)
              .andExpect(jsonPath("$[0].reachable").exists())
              .andReturn();
  
      // Both vuln in foo and bar are reported
      vuln_deps =
          (Set<HubIntegrationController.VulnerableItemDependency>)
              JacksonUtil.asObject(response.getResponse().getContentAsString(), Set.class);
      assertEquals(1, vuln_deps.size());
    }
  
    @Test
    public void testGetHubAppWithSlashChar() throws Exception {
      // Rest-post http-client 4.1.3
      Library lib =
          (Library)
              JacksonUtil.asObject(
                  FileUtil.readFile(Paths.get("./src/test/resources/dummy_app/lib.json")),
                  Library.class);
      this.libRepository.customSave(lib);
  
      // Rest-post bug
      final Bug bug =
          (Bug)
              JacksonUtil.asObject(
                  FileUtil.readFile(Paths.get("./src/test/resources/dummy_app/bug_foo.json")),
                  Bug.class);
      this.bugRepository.customSave(bug, true);
  
      // Rest-post app using http-client
      final Application app = new Application(APP_GROUP, APP_ARTIFACT, "0.0./r" + APP_VERSION);
  
      // Dependencies
      final Set<Dependency> app_dependency = new HashSet<Dependency>();
      app_dependency.add(new Dependency(app, lib, Scope.COMPILE, false, "foo.jar"));
      String token = spaceRepository.getDefaultSpace(null).getSpaceToken();
      app.setSpace(spaceRepository.getDefaultSpace(null));
  
      app.setDependencies(app_dependency);
      this.appRepository.customSave(app);
  
      // Read all apps
      MvcResult result =
          mockMvc.perform(get("/hubIntegration/apps/")).andExpect(status().isOk()).andReturn();
  
      String item = result.getResponse().getContentAsString();
      item = item.substring(2, item.length() - 2);
  
      // Read all public apps
      mockMvc
          .perform(get("/hubIntegration/apps/" + item + "/vulndeps"))
          .andExpect(status().isOk())
          .andExpect(jsonPath("$[0].spaceToken").exists())
          .andExpect(jsonPath("$[0].appId").exists())
          .andExpect(jsonPath("$[0].lastScan").exists())
          .andExpect(jsonPath("$[0].reachable", is(false)))
          .andReturn();
    }
  
    public static String getAppUri(Application _app) {
      return "/apps/" + _app.getMvnGroup() + "/" + _app.getArtifact() + "/" + _app.getVersion();
    }
  
    public static String getAppsExportUri(String _format) {
      return "/apps/export?format=" + _format;
    }
  
    private void createTenantAndSpace(String _tenant, String _space, boolean _default) {
      Tenant default_tenant = null;
      try {
        default_tenant =
            TenantRepository.FILTER.findOne(tenantRepository.findBySecondaryKey(_tenant));
      } catch (EntityNotFoundException e) {
        default_tenant = new Tenant();
        default_tenant.setTenantToken(_tenant);
        default_tenant.setTenantName(_tenant);
        default_tenant.setDefault(_default);
        tenantRepository.save(default_tenant);
        default_tenant =
            TenantRepository.FILTER.findOne(tenantRepository.findBySecondaryKey(_tenant));
      }
  
      Space default_space = null;
      try {
        default_space = SpaceRepository.FILTER.findOne(spaceRepository.findBySecondaryKey(_space));
      } catch (EntityNotFoundException e) {
        default_space = new Space();
        default_space.setSpaceName(_space);
        default_space.setSpaceToken(_space);
        default_space.setDefault(_default);
        default_space.setExportConfiguration(ExportConfiguration.DETAILED);
        default_space.setSpaceDescription("bar");
        default_space.setSpaceOwners(
            new HashSet<String>(Arrays.asList(new String[] {"foo@bar.com"})));
        default_space.setTenant(default_tenant);
        spaceRepository.save(default_space);
      }
    }
  }
  