mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-14 12:06:54 +00:00
parent
5f8e2a7f4f
commit
ea918495ff
46
.github/workflows/security-scan.yml
vendored
Normal file
46
.github/workflows/security-scan.yml
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
|
||||
# Copyright 2021 Collate
|
||||
# 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.
|
||||
|
||||
name: security-scan
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
security-scan:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.9
|
||||
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
python3 -m venv env
|
||||
source env/bin/activate
|
||||
make install_all install_apis
|
||||
|
||||
- name: Maven build
|
||||
run: mvn -DskipTests clean package
|
||||
|
||||
- name: Run Scan
|
||||
run: |
|
||||
source env/bin/activate
|
||||
make snyk-report
|
57
Makefile
57
Makefile
@ -196,7 +196,6 @@ install_antlr_cli: ## Install antlr CLI locally
|
||||
curl https://www.antlr.org/download/antlr-4.9.2-complete.jar >> /usr/local/bin/antlr4
|
||||
chmod 755 /usr/local/bin/antlr4
|
||||
|
||||
|
||||
.PHONY: docker-docs
|
||||
docker-docs: ## Runs the OM docs in docker passing openmetadata-docs as volume for content and images
|
||||
docker run --name openmetadata-docs -p 3000:3000 -v ${PWD}/openmetadata-docs/content:/docs/content/ -v ${PWD}/openmetadata-docs/images:/docs/public/images -v ${PWD}/openmetadata-docs/ingestion:/docs/public/ingestion openmetadata/docs:latest
|
||||
@ -208,3 +207,59 @@ docker-docs-validate: ## Runs the OM docs in docker passing openmetadata-docs a
|
||||
.PHONY: docker-docs-local
|
||||
docker-docs-local: ## Runs the OM docs in docker with a local image
|
||||
docker run --name openmetadata-docs -p 3000:3000 -v ${PWD}/openmetadata-docs/content:/docs/content/ -v ${PWD}/openmetadata-docs/images:/docs/public/images -v ${PWD}/openmetadata-docs/ingestion:/docs/public/ingestion openmetadata-docs:local
|
||||
|
||||
|
||||
## SNYK
|
||||
SNYK_ARGS := --severity-threshold=high
|
||||
|
||||
.PHONY: snyk-ingestion-report
|
||||
snyk-ingestion-report: ## Uses Snyk CLI to validate the ingestion code and container. Don't stop the execution
|
||||
@echo "Validating Ingestion container..."
|
||||
docker build -t openmetadata-ingestion:scan -f ingestion/Dockerfile .
|
||||
snyk container test openmetadata-ingestion:scan --file=ingestion/Dockerfile $(SNYK_ARGS) >> security/ingestion-docker-scan.out | true;
|
||||
@echo "Validating ALL ingestion dependencies. Make sure the venv is activated."
|
||||
cd ingestion; \
|
||||
pip freeze > scan-requirements.txt; \
|
||||
snyk test --file=scan-requirements.txt --package-manager=pip --command=python3 $(SNYK_ARGS) >> ../security/ingestion-dep-scan.out | true; \
|
||||
snyk code test $(SNYK_ARGS) >> ../security/ingestion-code-scan.out | true;
|
||||
|
||||
.PHONY: snyk-airflow-apis-report
|
||||
snyk-airflow-apis-report: ## Uses Snyk CLI to validate the airflow apis code. Don't stop the execution
|
||||
@echo "Validating airflow dependencies. Make sure the venv is activated."
|
||||
cd openmetadata-airflow-apis; \
|
||||
snyk code test $(SNYK_ARGS) >> ../security/airflow-apis-code-scan.out | true;
|
||||
|
||||
.PHONY: snyk-catalog-report
|
||||
snyk-server-report: ## Uses Snyk CLI to validate the catalog code and container. Don't stop the execution
|
||||
@echo "Validating catalog container... Make sure the code is built and available under openmetadata-dist"
|
||||
docker build -t openmetadata-server:scan -f docker/local-metadata/Dockerfile .
|
||||
snyk container test openmetadata-server:scan --file=docker/local-metadata/Dockerfile $(SNYK_ARGS) >> security/server-docker-scan.out | true;
|
||||
snyk test --all-projects $(SNYK_ARGS) >> security/server-dep-scan.out | true;
|
||||
snyk code test --all-projects $(SNYK_ARGS) >> security/server-code-scan.out | true;
|
||||
|
||||
.PHONY: snyk-ui-report
|
||||
snyk-ui-report: ## Uses Snyk CLI to validate the UI dependencies. Don't stop the execution
|
||||
snyk test --file=openmetadata-ui/src/main/resources/ui/yarn.lock $(SNYK_ARGS) >> security/ui-dep-scan.out | true;
|
||||
|
||||
.PHONY: snyk-dependencies-report
|
||||
snyk-dependencies-report: ## Uses Snyk CLI to validate the project dependencies: MySQL, Postgres and ES
|
||||
@echo "Validating dependencies images..."
|
||||
snyk container test mysql/mysql-server:latest $(SNYK_ARGS) >> security/mysql-scan.out | true;
|
||||
snyk container test postgres:latest $(SNYK_ARGS) >> security/postgres-scan.out | true;
|
||||
snyk container test docker.elastic.co/elasticsearch/elasticsearch:7.10.2 $(SNYK_ARGS) >> security/es-scan.out | true;
|
||||
|
||||
.PHONY: snyk-report
|
||||
snyk-report: ## Uses Snyk CLI to run a security scan of the different pieces of the code
|
||||
@echo "To run this locally, make sure to install and authenticate using the Snyk CLI: https://docs.snyk.io/snyk-cli/install-the-snyk-cli"
|
||||
mkdir -p security
|
||||
$(MAKE) snyk-ingestion-report
|
||||
$(MAKE) snyk-airflow-apis-report
|
||||
$(MAKE) snyk-server-report
|
||||
$(MAKE) snyk-ui-report
|
||||
$(MAKE) snyk-dependencies-report
|
||||
$(MAKE) read-report
|
||||
|
||||
.PHONY: read-report
|
||||
read-report: ## Read files from security/
|
||||
@echo "Reading all results"
|
||||
ls security | xargs -I % cat security/%
|
||||
|
@ -1,70 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2021 Collate
|
||||
~ 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.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>catalog</artifactId>
|
||||
<groupId>org.open-metadata</groupId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>catalog-docker</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<profiles>
|
||||
<!-- Profile for building official Docker images. Not bound to build phases since that would require anyone build to have the Docker engine installed on their machine -->
|
||||
<profile>
|
||||
<id>docker</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.spotify</groupId>
|
||||
<artifactId>docker-maven-plugin</artifactId>
|
||||
<version>0.4.13</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>build</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<imageName>catalog</imageName>
|
||||
<dockerDirectory>${project.basedir}/images/catalog</dockerDirectory>
|
||||
<imageTags>
|
||||
<imageTag>${parent.version}</imageTag>
|
||||
</imageTags>
|
||||
<buildArgs>
|
||||
<REGISTRY_VERSION>${parent.version}</REGISTRY_VERSION>
|
||||
</buildArgs>
|
||||
<resources>
|
||||
<resource>
|
||||
<targetPath>/</targetPath>
|
||||
<directory>${project.basedir}/../dist/target</directory>
|
||||
<include>catalog-${parent.version}.tar.gz</include>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
@ -1,22 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""OpenMetadata Server for generated reports."""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
"""Run OpenMetadata report server."""
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "openmetadata.settings")
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
) from exc
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,16 +0,0 @@
|
||||
"""
|
||||
ASGI config for openmetadata project.
|
||||
|
||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "metadata_server.openmetadata.settings")
|
||||
|
||||
application = get_asgi_application()
|
@ -1,120 +0,0 @@
|
||||
from pathlib import Path
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = "django-insecure-d+ngmo&w9xlfb)=x0)q-93*bb66r2hkhz$-@^=+8*30553@z@e"
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = False
|
||||
|
||||
ALLOWED_HOSTS = ["*"]
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
"django.contrib.admin",
|
||||
"django.contrib.auth",
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
]
|
||||
|
||||
ROOT_URLCONF = "metadata_server.openmetadata.urls"
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"DIRS": [BASE_DIR / "templates"],
|
||||
"APP_DIRS": True,
|
||||
"OPTIONS": {
|
||||
"context_processors": [
|
||||
"django.template.context_processors.debug",
|
||||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = "metadata_server.openmetadata.wsgi.application"
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.sqlite3",
|
||||
"NAME": BASE_DIR / "db.sqlite3",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
||||
},
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
|
||||
},
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
|
||||
},
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/3.2/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = "en-us"
|
||||
|
||||
TIME_ZONE = "UTC"
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/3.2/howto/static-files/
|
||||
|
||||
STATIC_URL = "/static/"
|
||||
STATICFILES_DIRS = [
|
||||
BASE_DIR / "static",
|
||||
]
|
||||
|
||||
|
||||
MEDIA_URL = "/media/"
|
||||
MEDIA_ROOT = BASE_DIR / "media"
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
@ -1,11 +0,0 @@
|
||||
from django.contrib import admin
|
||||
from django.urls import path
|
||||
|
||||
from .views import detail_page, list_page
|
||||
|
||||
urlpatterns = [
|
||||
path("admin/", admin.site.urls),
|
||||
path("", list_page),
|
||||
path("dataset/<str:fqn>/", detail_page, name="dataset"),
|
||||
]
|
||||
handler404 = "metadata_server.openmetadata.views.page_not_found_view"
|
@ -1,37 +0,0 @@
|
||||
import json
|
||||
import logging
|
||||
|
||||
from django.shortcuts import render
|
||||
|
||||
logger: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
with open("/tmp/datasets.json", "r") as d:
|
||||
data = json.load(d)
|
||||
datasets = []
|
||||
for dataset in data:
|
||||
if "table" in dataset:
|
||||
datasets.append(dataset["table"])
|
||||
elif "dashboard" in dataset:
|
||||
datasets.append(dataset["dashboard"])
|
||||
elif "topic" in dataset:
|
||||
datasets.append(dataset["topic"])
|
||||
else:
|
||||
logger.info("unrecognized element {}".format(dataset))
|
||||
|
||||
|
||||
def list_page(request):
|
||||
template_name = "list.html"
|
||||
return render(request, template_name, {"datasets": datasets})
|
||||
|
||||
|
||||
def detail_page(request, fqn):
|
||||
template_name = "dataset.html"
|
||||
dataset = list(filter(lambda data: data["fullyQualifiedName"] == fqn, datasets))
|
||||
if len(dataset) > 0:
|
||||
return render(request, template_name, {"dataset": dataset[0]})
|
||||
else:
|
||||
return render(request, template_name, {"error": "No Data Found"})
|
||||
|
||||
|
||||
def page_not_found_view(request, exception):
|
||||
return render(request, "404.html", status=404)
|
@ -1,16 +0,0 @@
|
||||
"""
|
||||
WSGI config for openmetadata project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "metadata_server.openmetadata.settings")
|
||||
|
||||
application = get_wsgi_application()
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Before Width: | Height: | Size: 9.3 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.1 KiB |
@ -1,12 +0,0 @@
|
||||
<svg width="250" height="300" viewBox="0 0 250 300" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M250 63H0V237.5V238H0.00391658C0.541243 272.288 56.298 300 125 300C193.702 300 249.459 272.288 249.996 238H250V237.5V63Z" fill="url(#paint0_linear)"/>
|
||||
<ellipse cx="125" cy="62.5" rx="125" ry="62.5" fill="#8D6AF1"/>
|
||||
<ellipse cx="125.5" cy="63.25" rx="62.5" ry="31.25" fill="white"/>
|
||||
<path d="M85 214.793V254.384C85 258.223 80.8526 260.63 77.5193 258.725L45.5193 240.44C43.9614 239.549 43 237.893 43 236.098V141.887C43 137.988 47.2635 135.589 50.5956 137.613L121.936 180.942C123.514 181.901 125.492 181.912 127.081 180.97L200.45 137.476C203.783 135.5 208 137.902 208 141.777V237.975C208 239.835 206.968 241.541 205.32 242.404L173.32 259.166C169.991 260.91 166 258.495 166 254.737V214.577C166 210.747 161.869 208.339 158.536 210.226L127.026 228.07C125.464 228.954 123.548 228.934 122.006 228.016L92.5565 210.496C89.2236 208.513 85 210.915 85 214.793Z" fill="white"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear" x1="125" y1="90" x2="125" y2="300" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#35198E"/>
|
||||
<stop offset="1" stop-color="#8D6AF1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.2 KiB |
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 20 KiB |
@ -1,9 +0,0 @@
|
||||
{% extends 'layout/base.html' %}
|
||||
{% load static %}
|
||||
{% block content %}
|
||||
<div class="tw-flex tw-flex-col tw-place-items-center tw-mt-24" data-testid="no-page-found">
|
||||
<div class="tw-flex tw-flex-col tw-mt-24 tw-place-items-center" data-testid="error"> <img data-testid="no-data-found" src="{% static 'img/no-data-found.svg' %}" width="500"></div>
|
||||
<div class="tw-flex tw-mt-3 tw-justify-around" data-testid="route-links"><a class="tw-mr-2" href="/"><button class="tw-align-bottom tw-inline-flex tw-items-center tw-justify-center tw-cursor-pointer
|
||||
tw-leading-5 tw-transition-colors tw-duration-150 tw-font-medium focus:tw-outline-none tw-px-4 tw-py-2 tw-rounded-lg tw-text-sm tw-text-white tw-bg-primary tw-border tw-border-transparent active:tw-bg-primary-active hover:tw-bg-primary-hover focus:tw-bg-primary-active focus:tw-ring focus:tw-ring-purple-300 " type="button">Go to Home</button></a></div>
|
||||
</div>
|
||||
{% endblock content %}
|
File diff suppressed because one or more lines are too long
@ -1,21 +0,0 @@
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="shortcut icon" href="{% static 'img/favicon.png' %}" type="image/png" />
|
||||
<link rel="stylesheet" href="{% static 'css/style.css' %}">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.css">
|
||||
<title>{% block title %}{% endblock title %}</title>
|
||||
</head>
|
||||
<body>
|
||||
{% include "layout/header.html" %}
|
||||
<div class="main-container">
|
||||
<div class="content-wrapper">
|
||||
{% block content %}{% endblock content %}
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -1,54 +0,0 @@
|
||||
{% load static %}
|
||||
<div class="tw-h-14 tw-py-2 tw-px-5 tw-border-b-2 tw-border-separator">
|
||||
<div class="tw-flex tw-items-center tw-flex-row tw-justify-between tw-flex-nowrap">
|
||||
<div class="tw-flex tw-items-center tw-flex-row tw-justify-between tw-flex-nowrap tw-mr-auto">
|
||||
<a aria-current="page" class="active" href="/"><img alt="OpenMetadata Logo" class="svg-icon " src="{% static 'img/logo.svg' %}" width="30"></a>
|
||||
<div class="tw-flex-none tw-relative tw-pl-5 " data-testid="appbar-item"><span class="fa fa-search tw-absolute tw-block tw-z-10 tw-w-9 tw-mt-2 tw-h-8 tw-leading-8 tw-text-center tw-pointer-events-none tw-text-gray-400"></span><input class="tw-relative search-grey tw-rounded tw-border tw-border-main tw-bg-body-main focus:tw-outline-none tw-pl-8 tw-py-1" type="text" value=""></div>
|
||||
<div class="tw-ml-9">
|
||||
<a class="tw-nav focus:tw-no-underline" data-testid="appbar-item" href="/explore" style="color: rgb(55, 53, 47);">Explore</a>
|
||||
<div class="tw-relative tw-inline-block tw-text-left false" data-testid="dropdown-item">
|
||||
<div>
|
||||
<button aria-expanded="true" aria-haspopup="true" class="tw-inline-flex tw-px-4 tw-py-2 focus:tw-outline-none tw-justify-center tw-nav " data-testid="menu-button" id="menu-button" type="button">
|
||||
<p class="hover:tw-underline" style="color: rgb(55, 53, 47);">Settings</p>
|
||||
<svg aria-hidden="true" class="tw-inline-block tw-h-5 tw-w-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" style="margin-top: 1px; color: rgb(55, 53, 47);">
|
||||
<path clip-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1
|
||||
0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="tw-nav focus:tw-no-underline hover:tw-underline"><img alt="Doc icon" class="svg-icon tw-align-middle tw--mt-0.5 tw-mr-1" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHhtbG5zOnN2Z2pzPSJodHRwOi8vc3ZnanMuY29tL3N2Z2pzIiB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgeD0iMCIgeT0iMCIgdmlld0JveD0iMCAwIDUxMiA1MTIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDUxMiA1MTIiIHhtbDpzcGFjZT0icHJlc2VydmUiIGZpbGw9IiMzNzM1MmYiPgoJPGc+PGcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KCQk8cGF0aCBkPSJtNDUxLjA4MSAzMDEuMTcxYy01Ljg1OC01Ljg1Ny0xNS4zNTUtNS44NTctMjEuMjEzIDBzLTUuODU4IDE1LjM1NSAwIDIxLjIxM2wzMC4yNTkgMzAuMjU5YzIuOTI5IDIuOTI5IDYuNzY3IDQuMzkzIDEwLjYwNiA0LjM5M3M3LjY3OC0xLjQ2NSAxMC42MDctNC4zOTNjNS44NTgtNS44NTcgNS44NTgtMTUuMzU1IDAtMjEuMjEzeiIgZGF0YS1vcmlnaW5hbD0iIzAwMDAwMCIgc3R5bGU9IiI+PC9wYXRoPgoJCTxwYXRoIGQ9Im00NDAuNDc0IDE3Ny4wMzdjMy44MzggMCA3LjY3OC0xLjQ2NSAxMC42MDctNC4zOTNsMzAuMjU5LTMwLjI1OWM1Ljg1OC01Ljg1NyA1Ljg1OC0xNS4zNTUgMC0yMS4yMTNzLTE1LjM1NS01Ljg1OC0yMS4yMTMgMGwtMzAuMjU5IDMwLjI1OWMtNS44NTggNS44NTctNS44NTggMTUuMzU1IDAgMjEuMjEzIDIuOTI5IDIuOTI5IDYuNzY3IDQuMzkzIDEwLjYwNiA0LjM5M3oiIGRhdGEtb3JpZ2luYWw9IiMwMDAwMDAiIHN0eWxlPSIiPjwvcGF0aD4KCQk8cGF0aCBkPSJtNDk3IDIyMS45MDdoLTQyLjc5M2MtOC4yODQgMC0xNSA2LjcxNi0xNSAxNXM2LjcxNiAxNSAxNSAxNWg0Mi43OTNjOC4yODQgMCAxNS02LjcxNiAxNS0xNXMtNi43MTYtMTUtMTUtMTV6IiBkYXRhLW9yaWdpbmFsPSIjMDAwMDAwIiBzdHlsZT0iIj48L3BhdGg+CgkJPHBhdGggZD0ibTM5OS4yNDcgMjMuNTg4Yy00LjczNi0yLjY1My0xMC41MzYtMi41MzktMTUuMTY1LjI5NWwtMTcxLjc4NSAxMDUuMjA1aC03Ni4yNjFjLTE2LjI4Ny0uODYxLTMxLjc4MSA0LjgxNS00My42NzUgMTYuMDExLTQuNDI3IDQuMTY2LTguMTU5IDguOTc1LTExLjEzMyAxNC4yMTVoLTM3Ljc0MWMtMjMuOTc5IDAtNDMuNDg3IDE5LjUwOC00My40ODcgNDMuNDg3djY4LjIxMmMwIDIzLjk3OSAxOS41MDggNDMuNDg3IDQzLjQ4NyA0My40ODdoMzcuNjY2YzYuMDQgMTAuNzk4IDE1LjE1OCAxOS43NDkgMjYuMjQzIDI1LjY3M2wtNTIuMjYxIDkwLjUyYy0yLjAwNSAzLjQ3My0yLjUzNiA3LjYwNC0xLjQ3NSAxMS40NzIgMS4wNjIgMy44NjcgMy42MjggNy4xNDggNy4xMjUgOS4xMWw2Ni4xODIgMzcuMTI5YzIuMzIgMS4zMDIgNC44NCAxLjkyMSA3LjMyNiAxLjkyMSA1LjE4NyAwIDEwLjIyOC0yLjY5NSAxMy4wMDMtNy41MDNsNzQuNjI2LTEyOS4yNTggMTYyLjMzIDk2LjQ2OWMyLjM2IDEuNDAzIDUuMDExIDIuMTA1IDcuNjYzIDIuMTA1IDIuNTU4IDAgNS4xMTctLjY1MyA3LjQxOC0xLjk2MyA0LjY4Ny0yLjY2NyA3LjU4Mi03LjY0NSA3LjU4Mi0xMy4wMzd2LTQwMC40NjNjMC01LjQyOC0yLjkzMy0xMC40MzMtNy42NjgtMTMuMDg3em0tMzU1Ljc2IDI2MC45MTJjLTcuNDM3IDAtMTMuNDg3LTYuMDUtMTMuNDg3LTEzLjQ4N3YtNjguMjEyYzAtNy40MzcgNi4wNS0xMy40ODcgMTMuNDg3LTEzLjQ4N2gyOS42NTRjLS4wMDIuMjI0LS4wMTcuNDQ3LS4wMTcuNjcxdjkzLjg0NGMwIC4yMjUuMDE0LjQ0Ny4wMTcuNjcxem01OS42MzctOTQuNTE1YzAtOC43ODIgMy41NzEtMTcuMTggOS43OTctMjMuMDQgNS45MjYtNS41NzcgMTMuNjU4LTguMzgyIDIxLjc4My03Ljg4NC4zMDMuMDE4IDY2LjgyMy4wMjcgNjYuODIzLjAyN3YxNTguODJoLTY1LjQyMmMtMTguNTI5LTEuMzMxLTMyLjk4MS0xNi4yMjQtMzIuOTgxLTM0LjA4em0yNS42IDI2NS4wMDYtNDAuMDE2LTIyLjQ0OSA0Ny45NzktODMuMTAzYy4yODktLjUuNTM4LTEuMDEzLjc2My0xLjUzaDUzLjA5N3ptMjQ4LjE5MS00NC4yMTYtMTQ1LjM4OC04Ni40MDF2LTE3MS44ODNsMTQ1LjM4OC04OS4wNHoiIGRhdGEtb3JpZ2luYWw9IiMwMDAwMDAiIHN0eWxlPSIiPjwvcGF0aD4KCTwvZz48L2c+Cjwvc3ZnPg==" width="16"><span>What's new</span></button>
|
||||
<div>
|
||||
<div class="tw-relative tw-inline-block tw-text-left false" data-testid="dropdown-item">
|
||||
<div>
|
||||
<button aria-expanded="true" aria-haspopup="true" class="tw-inline-flex tw-px-4 tw-py-2 focus:tw-outline-none tw-justify-center tw-nav " data-testid="menu-button" id="menu-button" type="button">
|
||||
<img alt="Doc icon" class="svg-icon tw-align-middle tw-mt-0.5 tw-mr-1" src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDUxMiA1MTIiIGZpbGw9IiMzNzM1MmYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik0yNTYgMEMxMTQuNTA5IDAgMCAxMTQuNDk2IDAgMjU2QzAgMzk3LjQ4OSAxMTQuNDk2IDUxMiAyNTYgNTEyQzM5Ny40OTEgNTEyIDUxMiAzOTcuNTA0IDUxMiAyNTZDNTEyIDExNC41MDkgMzk3LjUwNCAwIDI1NiAwWk0yNTYgNDc2LjI3OUMxMzQuNTM4IDQ3Ni4yNzkgMzUuNzIxIDM3Ny40NjMgMzUuNzIxIDI1NkMzNS43MjEgMTM0LjUzNyAxMzQuNTM4IDM1LjcyMSAyNTYgMzUuNzIxQzM3Ny40NjMgMzUuNzIxIDQ3Ni4yNzkgMTM0LjUzNyA0NzYuMjc5IDI1NkM0NzYuMjc5IDM3Ny40NjMgMzc3LjQ2MyA0NzYuMjc5IDI1NiA0NzYuMjc5WiIgZmlsbD0iIzM3MzUyZiIvPgo8cGF0aCBkPSJNMjQ4LjQyNSAzMjMuOTI0QzIzNC4yNzIgMzIzLjkyNCAyMjIuODE1IDMzNS43MTggMjIyLjgxNSAzNDkuODdDMjIyLjgxNSAzNjMuNjg3IDIzMy45MzUgMzc1LjgxOCAyNDguNDI1IDM3NS44MThDMjYyLjkxNSAzNzUuODE4IDI3NC4zNzEgMzYzLjY4NyAyNzQuMzcxIDM0OS44N0MyNzQuMzcxIDMzNS43MTggMjYyLjU3NyAzMjMuOTI0IDI0OC40MjUgMzIzLjkyNFoiIGZpbGw9IiMzNzM1MmYiLz4KPHBhdGggZD0iTTI1Mi44MDUgMTI3LjQ2OUMyMDcuMzEzIDEyNy40NjkgMTg2LjQyMSAxNTQuNDI4IDE4Ni40MjEgMTcyLjYyNEMxODYuNDIxIDE4NS43NjYgMTk3LjU0MSAxOTEuODMyIDIwNi42MzkgMTkxLjgzMkMyMjQuODM2IDE5MS44MzIgMjE3LjQyMyAxNjUuODg0IDI1MS43OTQgMTY1Ljg4NEMyNjguNjQyIDE2NS44ODQgMjgyLjEyMiAxNzMuMjk4IDI4Mi4xMjIgMTg4Ljc5OUMyODIuMTIyIDIwNi45OTUgMjYzLjI1MSAyMTcuNDQxIDI1Mi4xMzEgMjI2Ljg3NkMyNDIuMzU4IDIzNS4yOTkgMjI5LjU1NCAyNDkuMTE2IDIyOS41NTQgMjc4LjA5NkMyMjkuNTU0IDI5NS42MTggMjM0LjI3MiAzMDAuNjczIDI0OC4wODcgMzAwLjY3M0MyNjQuNTk4IDMwMC42NzMgMjY3Ljk2OCAyOTMuMjYgMjY3Ljk2OCAyODYuODU2QzI2Ny45NjggMjY5LjMzNCAyNjguMzA1IDI1OS4yMjUgMjg2LjgzOSAyNDQuNzM1QzI5NS45MzcgMjM3LjY1OSAzMjQuNTc5IDIxNC43NDQgMzI0LjU3OSAxODMuMDY5QzMyNC41NzkgMTUxLjM5NCAyOTUuOTM3IDEyNy40NjkgMjUyLjgwNSAxMjcuNDY5WiIgZmlsbD0iIzM3MzUyZiIvPgo8L3N2Zz4K" width="16">
|
||||
<p class="hover:tw-underline" style="color: rgb(55, 53, 47);">Need Help</p>
|
||||
<svg aria-hidden="true" class="tw-inline-block tw-h-5 tw-w-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" style="margin-top: 1px; color: rgb(55, 53, 47);">
|
||||
<path clip-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1
|
||||
0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div data-testid="dropdown-profile">
|
||||
<div class="tw-relative tw-inline-block tw-text-left false" data-testid="dropdown-item">
|
||||
<div>
|
||||
<button aria-expanded="true" aria-haspopup="true" class="tw-inline-flex tw-px-4 tw-py-2 focus:tw-outline-none tw-justify-center tw-nav " data-testid="menu-button" id="menu-button" type="button">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="tw-mr-1" style="height: 22px; width: 22px; border-radius: 50%;">
|
||||
<path d="M511.676 498.752l-12.8-51.2a74.345 74.345 0 00-48.747-52.885l-93.867-31.275c-22.891-9.536-33.365-46.4-35.627-60.395a88.703 88.703 0 0031.36-57.664 17.391 17.391 0 012.581-10.987 10.665 10.665 0 007.317-6.4 159.447 159.447 0 0011.435-46.613 10.254 10.254 0 00-.32-2.581 27.434 27.434 0 00-10.347-15.531v-56.555c0-34.368-10.496-48.469-21.547-56.64C339.004 33.472 321.276 0 255.996 0c-57.917 2.332-104.335 48.75-106.667 106.667v56.555a27.434 27.434 0 00-10.347 15.531 10.26 10.26 0 00-.32 2.581 159.454 159.454 0 0011.435 46.635 9.067 9.067 0 006.4 6.123c1.195.597 3.435 3.691 3.435 11.243a88.683 88.683 0 0031.531 57.771c-2.24 13.973-12.651 50.816-34.901 60.117l-94.699 31.445a74.346 74.346 0 00-48.725 52.843l-12.8 51.2c-1.449 5.71 2.005 11.514 7.715 12.963.853.217 1.73.327 2.61.328H501.33c5.891-.002 10.665-4.779 10.664-10.67a10.722 10.722 0 00-.318-2.58z" fill="#455a64"></path>
|
||||
</svg>
|
||||
<p class="hover:tw-underline" style="color: rgb(55, 53, 47);"><span>Welcome, <span class="tw-font-medium">Aaron</span></span></p>
|
||||
<svg aria-hidden="true" class="tw-inline-block tw-h-5 tw-w-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" style="margin-top: 1px; color: rgb(55, 53, 47);">
|
||||
<path clip-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1
|
||||
0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user