Scan report
hatchet-dev / hatchet
Could an attacker exploit mistakes in this code?
Minor issues. Worth a glance, unlikely to be urgent.
- High
- 0
- Medium
- 4
- Low
- 12
Sources: Bandit, Semgrep, pip-audit
Does this code look like it is trying to cause harm?
Serious issues. Review the high-severity findings before using it.
- High
- 11
- Medium
- 3
- Low
- 10
Sources: YARA, ClamAV, GuardDog, dependency checker
- Likely real
- 13
- Uncertain
- 5
- Likely noise
- 22
The risk classifier rates every finding for how likely it is to be a real issue. Likely noise still counts, just much less.
What we found, in plain English
13 findings likely need attention, 22 look like noise. Most notable: Detects download-and-execute patterns: fetching a remote file then executing it [threat... in /sdks/python/hatchet_sdk/embedded.py (95% confidence).
Detections by engine
Score over time
Overall score each time this repository was scanned.
Findings
Highest severity first. Select a finding to see the code and what it means.
-
- Engine
- GuardDog · Supply-chain attack heuristics
- Counts towards
- Malicious pattern
- File
- /sdks/python/hatchet_sdk/embedded.py line 195
- Likely real
- Yes, 95% confidence
Detects download-and-execute patterns: fetching a remote file then executing it [threat-process-download-exec]
url = f"{REPO_URL}/releases/download/{tag}/checksums.txt" with urllib.request.urlopen(url, timeout=10) as res: checksums: str = res.read().decode() -
- Engine
- GuardDog · Supply-chain attack heuristics
- Counts towards
- Malicious pattern
- File
- /frontend/app/src/pages/main/v1/overview/components/onboarding-steps.tsx line 521
- Likely real
- Yes, 95% confidence
Detects download-and-execute patterns: fetching a remote file then executing it [threat-process-download-exec]
className={codeBlockClass} code={`curl -fsSL https://install.hatchet.run/install.sh | bash`} language="shell" copy -
- Engine
- GuardDog · Supply-chain attack heuristics
- Counts towards
- Malicious pattern
- File
- /sdks/python/hatchet_sdk/embedded.py line 379
- Likely real
- Yes, 95% confidence
Detects download-and-execute patterns: fetching a remote file then executing it [threat-process-download-exec]
# the sidecar shuts down when its stdin closes, so it never outlives this process process = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.DEVNULL) atexit.register(process.terminate) -
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /sdks/python/hatchet_sdk/embedded.py
- Likely real
- Probably noise, 1% confidence
YARA rule matched: detect_download_and_execute — Downloads a remote resource AND executes it - the actually dangerous combination, not just downloading alone
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /README.md
- Likely real
- Probably noise, 1% confidence
YARA rule matched: detect_download_and_execute — Downloads a remote resource AND executes it - the actually dangerous combination, not just downloading alone
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /cmd/hatchet-cli/cli/skill-assets/hatchet-cli/references/setup-cli.md
- Likely real
- Probably noise, 1% confidence
YARA rule matched: detect_download_and_execute — Downloads a remote resource AND executes it - the actually dangerous combination, not just downloading alone
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /frontend/app/src/pages/main/v1/overview/components/onboarding-steps.tsx
- Likely real
- Probably noise, 1% confidence
YARA rule matched: detect_download_and_execute — Downloads a remote resource AND executes it - the actually dangerous combination, not just downloading alone
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /frontend/app/src/pages/main/v1/tenant-settings/organization/audit-log-settings.tsx
- Likely real
- Probably noise, 1% confidence
YARA rule matched: detect_download_and_execute — Downloads a remote resource AND executes it - the actually dangerous combination, not just downloading alone
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /.github/workflows/cli-release.yaml
- Likely real
- Probably noise, 1% confidence
YARA rule matched: detect_download_and_execute — Downloads a remote resource AND executes it - the actually dangerous combination, not just downloading alone
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /hack/install/install.sh
- Likely real
- Probably noise, 1% confidence
YARA rule matched: detect_download_and_execute — Downloads a remote resource AND executes it - the actually dangerous combination, not just downloading alone
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /hack/install/install.sh
- Likely real
- Probably noise, 0% confidence
YARA rule matched: detect_persistence_mechanism — Modifies a startup/scheduling location - legitimate application code essentially never does this
-
- Engine
- GuardDog · Supply-chain attack heuristics
- Counts towards
- Malicious pattern
- File
- /pkg/repository/payloadstore.go line 344
- Likely real
- Yes, 98% confidence
Detects unicode homoglyphs and uncommon characters used for obfuscation [threat-runtime-obfuscation-unicode]
ID int64 InsertedAtMicro int64 // Unix microseconds — avoids time.Time map-key pitfalls (mono clock, location, sub-μs precision) TenantId uuid.UUID Type sqlcv1.V1PayloadType -
- Engine
- GuardDog · Supply-chain attack heuristics
- Counts towards
- Malicious pattern
- File
- /sdks/ruby/src/lib/hatchet/config.rb line 131
- Likely real
- Yes, 96% confidence
Detects access to sensitive files (credentials, configs, keys) [threat-filesystem-read]
ENV_FILE_NAMES = [".env", ".env.hatchet", ".env.dev", ".env.local"].freeze # @!attribute token -
- Engine
- GuardDog · Supply-chain attack heuristics
- Counts towards
- Malicious pattern
- File
- /sdks/python/hatchet_sdk/config.py line 20
- Likely real
- Yes, 96% confidence
Detects access to sensitive files (credentials, configs, keys) [threat-filesystem-read]
env_prefix=env_prefix, env_file=(".env", ".env.hatchet", ".env.dev", ".env.local"), extra="ignore", ) -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /sdks/python/hatchet_sdk/embedded.py line 195
- Likely real
- Uncertain, 49% confidence
Audit url open for permitted schemes. Allowing use of file:/ or custom schemes is often unexpected.
194 url = f"{REPO_URL}/releases/download/{tag}/checksums.txt" 195 with urllib.request.urlopen(url, timeout=10) as res: 196 checksums: str = res.read().decode() -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /sdks/python/hatchet_sdk/embedded.py line 264
- Likely real
- Uncertain, 48% confidence
Audit url open for permitted schemes. Allowing use of file:/ or custom schemes is often unexpected.
263 try: 264 urllib.request.urlretrieve(url, tmp_path) 265 -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /hack/db/generate-oss-db-model.py line 383
- Likely real
- Uncertain, 46% confidence
Possible SQL injection vector through string-based query construction.
382 relationship_count = len(hatchet_oss_model["relationships"]) 383 return f"""<!doctype html> 384 <html lang="en"> 385 <head> 386 <meta charset="utf-8"> 387 <meta name="viewport" content="width=device-width,initial-scale=1"> 388 <title>hatchet-oss database model</title> 389 <style> 390 :root {{ 391 color-scheme: dark; 392 --bg: #0b1020; --panel: #11182b; --panel2: #182238; --line: #34435f; 393 --text: #e7edf7; --muted: #8fa0ba; --accent: #6ee7b7; --pk: #fbbf24; --fk: #60a5fa; 394 }} 395 * {{ box-sizing: border-box; }} 396 html, body {{ height: 100%; margin: 0; overflow: hidden; }} 397 body {{ background: var(--bg); color: var(--text); font: 13px Inter, ui-sans-serif, system-ui, sans-serif; }} 398 button, input, select {{ color: inherit; font: inherit; }} 399 header {{ 400 height: 58px; display: flex; align-items: center; gap: 10px; padding: 9px 14px; 401 background: #0d1425; border-bottom: 1px solid #27334b; 402 }} 403 h1 {{ font-size: 16px; margin: 0 10px 0 0; white-space: nowrap; }} 404 .search {{ width: min(380px, 30vw); padding: 8px 10px; border: 1px solid #35435c; border-radius: 7px; background: #111a2d; }} 405 select, button {{ border: 1px solid #35435c; border-radius: 7px; background: #172136; padding: 7px 10px; }} 406 button {{ cursor: pointer; }} 407 button:hover {{ background: #21304b; }} 408 .stats {{ margin-left: auto; color: var(--muted); white-space: nowrap; }} 409 .shell {{ height: calc(100% - 58px); display: grid; grid-template-columns: 260px 1fr 330px; }} 410 aside {{ background: var(--panel); min-width: 0; overflow: hidden; }} 411 .left {{ border-right: 1px solid #27334b; display: flex; flex-direction: column; }} 412 .right {{ border-left: 1px solid #27334b; overflow: auto; padding: 16px; }} 413 .source-note {{ padding: 11px 13px; color: var(--muted); border-bottom: 1px solid #27334b; line-height: 1.45; }} 414 .table-list {{ overflow: auto; padding: 7px; }} 415 .table-link {{ display: block; width: 100%; padding: 7px 9px; text-align: left; border: 0; background: none; color: var(--muted); }} 416 .table-link:hover, .table-link.active {{ color: var(--text); background: #1b2740; }} 417 .table-link small {{ float: right; color: #63728c; }} 418 #viewport {{ position: relative; overflow: hidden; cursor: grab; background-image: radial-gradient(#26334b 1px, transparent 1px); background-size: 24px 24px; }} 419 #viewport.dragging {{ cursor: grabbing; }} 420 #world {{ position: absolute; transform-origin: 0 0; }} 421 #edges {{ position: absolute; inset: 0; overflow: visible; pointer-events: none; }} 422 .edge {{ fill: none; stroke: #50698f; stroke-width: 1.5; opacity: .55; marker-end: url(#arrow); }} 423 .edge.active {{ stroke: var(--accent); stroke-width: 2.5; opacity: 1; }} 424 .group-label {{ position: absolute; color: #7787a1; font-size: 18px; font-weight: 700; letter-spacing: .04em; }} 425 .node {{ 426 position: absolute; width: 270px; border: 1px solid #3a4964; border-radius: 8px; 427 background: #121b2e; box-shadow: 0 5px 15px #0005; overflow: hidden; cursor: pointer; 428 }} 429 .node:hover, .node.selected {{ border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent), 0 6px 20px #0008; }} 430 .node.hidden, .edge.hidden, .group-label.hidden {{ display: none; }} 431 .node-title {{ padding: 8px 10px; background: var(--panel2); font-weight: 700; overflow: hidden; text-overflow: ellipsis; }} 432 .node-source {{ float: right; color: #71819c; font-size: 10px; font-weight: 500; }} 433 .column {{ display: grid; grid-template-columns: 19px minmax(0,1fr) auto; gap: 5px; padding: 3px 8px; border-top: 1px solid #1f2a40; font-size: 11px; }} 434 .key {{ color: var(--pk); font-weight: 700; }} 435 .fk {{ color: var(--fk); }} 436 .column-name {{ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }} 437 .column-type {{ color: var(--muted); max-width: 110px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }} 438 .more {{ padding: 4px 9px 6px; color: #71819c; font-size: 11px; }} 439 .empty {{ color: var(--muted); padding: 12px; }} 440 .right h2 {{ margin: 0 0 4px; font-size: 18px; overflow-wrap: anywhere; }} 441 .badge {{ display: inline-block; margin: 3px 4px 8px 0; padding: 2px 6px; border-radius: 10px; background: #25314a; color: #aab6c9; font-size: 10px; }} 442 .detail-source {{ color: var(--muted); margin-bottom: 14px; }} 443 .detail-column {{ padding: 8px 0; border-top: 1px solid #27334b; }} 444 .detail-column strong {{ display: block; overflow-wrap: anywhere; }} 445 .detail-type {{ color: #a8b6ca; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11px; overflow-wrap: anywhere; }} 446 .detail-meta {{ color: var(--muted); font-size: 11px; margin-top: 3px; overflow-wrap: anywhere; }} 447 .relations {{ margin-top: 16px; }} 448 .relation {{ padding: 6px 0; color: #a8b6ca; overflow-wrap: anywhere; }} 449 .relation button {{ border: 0; background: none; color: var(--fk); padding: 0; }} 450 @media (max-width: 1000px) {{ .shell {{ grid-template-columns: 220px 1fr; }} .right {{ display: none; }} }} 451 @media (max-width: 680px) {{ .shell {{ grid-template-columns: 1fr; }} .left {{ display: none; }} .stats {{ display: none; }} }} 452 </style> 453 </head> 454 <body> 455 <header> 456 <h1>hatchet-oss database model</h1> 457 <input id="search" class="search" type="search" placeholder="Search tables, columns, or types" aria-label="Search model"> 458 <select id="source" aria-label="Filter schema source"><option value="">All schema files</option></select> 459 <button id="zoomOut" title="Zoom out">−</button> 460 <button id="zoomIn" title="Zoom in">+</button> 461 <button id="fit">Fit</button> 462 <span class="stats">{table_count} tables · {relationship_count} foreign keys</span> 463 </header> 464 <main class="shell"> 465 <aside class="left"> 466 <div class="source-note">Generated from the canonical OSS SQL schema snapshots listed in sqlc.yaml. No database connection is used.</div> 467 <nav id="tableList" class="table-list" aria-label="Tables"></nav> 468 </aside> 469 <section id="viewport" aria-label="Entity relationship diagram"> 470 <div id="world"><svg id="edges"><defs><marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="5" markerHeight="5" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="#50698f"/></marker></defs></svg></div> 471 </section> 472 <aside id="details" class="right"><div class="empty">Select a table to inspect all columns and relationships.</div></aside> 473 </main> 474 <script> 475 const hatchetOssModel = {payload}; 476 const state = {{ scale: 0.55, x: 20, y: 20, selected: null, visible: new Set() }}; 477 const positions = new Map(); 478 const nodes = new Map(); 479 const edgeElements = []; 480 const world = document.getElementById('world'); 481 const viewport = document.getElementById('viewport'); 482 const list = document.getElementById('tableList'); 483 const details = document.getElementById('details'); 484 const search = document.getElementById('search'); 485 const sourceFilter = document.getElementById('source'); 486 487 for (const source of [...new Set(hatchetOssModel.tables.map(table => table.source))]) {{ 488 const option = document.createElement('option'); 489 option.value = source; 490 option.textContent = source; 491 sourceFilter.append(option); 492 }} 493 494 function layout() {{ 495 const groups = [...new Set(hatchetOssModel.tables.map(table => table.source))]; 496 let groupY = 45; 497 let worldWidth = 0; 498 for (const group of groups) {{ 499 const tables = hatchetOssModel.tables.filter(table => table.source === group); 500 const columns = Math.min(5, Math.max(2, Math.ceil(Math.sqrt(tables.length)))); 501 const heights = Array(columns).fill(groupY + 38); 502 const label = document.createElement('div'); 503 label.className = 'group-label'; 504 label.dataset.source = group; 505 label.style.left = '16px'; 506 label.style.top = `${{groupY}}px`; 507 label.textContent = group; 508 world.append(label); 509 for (const table of tables) {{ 510 const column = heights.indexOf(Math.min(...heights)); 511 const shown = Math.min(table.columns.length, 9); 512 const height = 37 + shown * 18 + (table.columns.length > shown ? 24 : 0); 513 positions.set(table.name, {{ x: 20 + column * 300, y: heights[column], width: 270, height }}); 514 heights[column] += height + 28; 515 }} 516 groupY = Math.max(...heights) + 70; 517 worldWidth = Math.max(worldWidth, columns * 300); 518 }} 519 world.dataset.width = worldWidth; 520 world.dataset.height = groupY; 521 }} 522 523 function keyLabel(column) {{ 524 if (column.primaryKey && column.foreignKey) return 'PK/FK'; 525 if (column.primaryKey) return 'PK'; 526 if (column.foreignKey) return 'FK'; 527 return ''; 528 }} 529 530 function createNodes() {{ 531 for (const table of hatchetOssModel.tables) {{ 532 const position = positions.get(table.name); 533 const node = document.createElement('article'); 534 node.className = 'node'; 535 node.dataset.name = table.name; 536 node.dataset.source = table.source; 537 node.style.cssText = `left:${{position.x}}px;top:${{position.y}}px;height:${{position.height}}px`; 538 const title = document.createElement('div'); 539 title.className = 'node-title'; 540 title.textContent = table.name; 541 const source = document.createElement('span'); 542 source.className = 'node-source'; 543 source.textContent = table.source.replace('.sql', ''); 544 title.append(source); 545 node.append(title); 546 for (const column of table.columns.slice(0, 9)) {{ 547 const row = document.createElement('div'); 548 row.className = 'column'; 549 const key = document.createElement('span'); 550 key.className = column.primaryKey ? 'key' : column.foreignKey ? 'fk' : ''; 551 key.textContent = keyLabel(column); 552 const name = document.createElement('span'); 553 name.className = 'column-name'; 554 name.textContent = column.name; 555 const type = document.createElement('span'); 556 type.className = 'column-type'; 557 type.textContent = column.type + (column.nullable ? '?' : ''); 558 row.append(key, name, type); 559 node.append(row); 560 }} 561 if (table.columns.length > 9) {{ 562 const more = document.createElement('div'); 563 more.className = 'more'; 564 more.textContent = `+ ${{table.columns.length - 9}} more columns`; 565 node.append(more); 566 }} 567 node.addEventListener('click', event => {{ 568 event.stopPropagation(); 569 selectTable(table.name, false); 570 }}); 571 world.append(node); 572 nodes.set(table.name, node); 573 }} 574 }} 575 576 function createEdges() {{ 577 const svg = document.getElementById('edges'); 578 svg.setAttribute('width', world.dataset.width); 579 svg.setAttribute('height', world.dataset.height); 580 for (const relationship of hatchetOssModel.relationships) {{ 581 const from = positions.get(relationship.source); 582 const to = positions.get(relationship.target); 583 if (!from || !to) continue; 584 const startX = from.x + from.width / 2; 585 const startY = from.y + from.height / 2; 586 const endX = to.x + to.width / 2; 587 const endY = to.y + to.height / 2; 588 const bend = Math.max(50, Math.abs(endX - startX) * 0.45); 589 const direction = endX >= startX ? 1 : -1; 590 const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); 591 path.setAttribute('d', `M ${{startX}} ${{startY}} C ${{startX + bend * direction}} ${{startY}}, ${{endX - bend * direction}} ${{endY}}, ${{endX}} ${{endY}}`); 592 path.setAttribute('class', 'edge'); 593 path.dataset.source = relationship.source; 594 path.dataset.target = relationship.target; 595 svg.append(path); 596 edgeElements.push(path); 597 }} 598 }} 599 600 function renderList() {{ 601 list.replaceChildren(); 602 const visibleTables = hatchetOssModel.tables.filter(table => state.visible.has(table.name)); 603 for (const table of visibleTables) {{ 604 const button = document.createElement('button'); 605 button.className = 'table-link' + (state.selected === table.name ? ' active' : ''); 606 button.textContent = table.name; 607 const count = document.createElement('small'); 608 count.textContent = table.columns.length; 609 button.append(count); 610 button.addEventListener('click', () => selectTable(table.name, true)); 611 list.append(button); 612 }} 613 if (!visibleTables.length) {{ 614 const empty = document.createElement('div'); 615 empty.className = 'empty'; 616 empty.textContent = 'No matching tables.'; 617 list.append(empty); 618 }} 619 }} 620 621 function renderDetails(table) {{ 622 details.replaceChildren(); 623 const heading = document.createElement('h2'); 624 heading.textContent = table.name; 625 const source = document.createElement('div'); 626 source.className = 'detail-source'; 627 source.textContent = table.source; 628 details.append(heading, source); 629 for (const column of table.columns) {{ 630 const row = document.createElement('div'); 631 row.className = 'detail-column'; 632 const name = document.createElement('strong'); 633 name.textContent = column.name; 634 const type = document.createElement('div'); 635 type.className = 'detail-type'; 636 type.textContent = column.type; 637 const meta = document.createElement('div'); 638 meta.className = 'detail-meta'; 639 const labels = [column.nullable ? 'nullable' : 'not null']; 640 if (column.primaryKey) labels.push('primary key'); 641 if (column.foreignKey) labels.push('foreign key'); 642 if (column.default !== null) labels.push(`default: ${{column.default}}`); 643 meta.textContent = labels.join(' · '); 644 row.append(name, type, meta); 645 details.append(row); 646 }} 647 const outgoing = hatchetOssModel.relationships.filter(item => item.source === table.name); 648 const incoming = hatchetOssModel.relationships.filter(item => item.target === table.name); 649 const section = document.createElement('div'); 650 section.className = 'relations'; 651 const title = document.createElement('strong'); 652 title.textContent = `Relationships (${{outgoing.length + incoming.length}})`; 653 section.append(title); 654 for (const relationship of outgoing) {{ 655 section.append(relationRow(`${{relationship.columns.join(', ')}} → `, relationship.target, relationship.targetColumns.join(', '))); 656 }} 657 for (const relationship of incoming) {{ 658 section.append(relationRow('← ', relationship.source, relationship.columns.join(', '))); 659 }} 660 if (!outgoing.length && !incoming.length) {{ 661 const empty = document.createElement('div'); 662 empty.className = 'empty'; 663 empty.textContent = 'No explicit foreign keys.'; 664 section.append(empty); 665 }} 666 details.append(section); 667 }} 668 669 function relationRow(prefix, target, suffix) {{ 670 const row = document.createElement('div'); 671 row.className = 'relation'; 672 row.append(document.createTextNode(prefix)); 673 const button = document.createElement('button'); 674 button.textContent = target; 675 button.addEventListener('click', () => selectTable(target, true)); 676 row.append(button, document.createTextNode(` (${{suffix}})`)); 677 return row; 678 }} 679 680 function selectTable(name, center) {{ 681 state.selected = name; 682 for (const [nodeName, node] of nodes) node.classList.toggle('selected', nodeName === name); 683 for (const edge of edgeElements) edge.classList.toggle('active', edge.dataset.source === name || edge.dataset.target === name); 684 const table = hatchetOssModel.tables.find(item => item.name === name); 685 if (table) renderDetails(table); 686 renderList(); 687 if (center && positions.has(name)) centerTable(name); 688 }} 689 690 function applyFilter() {{ 691 const query = search.value.trim().toLowerCase(); 692 const source = sourceFilter.value; 693 state.visible.clear(); 694 for (const table of hatchetOssModel.tables) {{ 695 const haystack = [table.name, table.source, ...table.columns.flatMap(column => [column.name, column.type])].join(' ').toLowerCase(); 696 const visible = (!source || table.source === source) && (!query || haystack.includes(query)); 697 nodes.get(table.name).classList.toggle('hidden', !visible); 698 if (visible) state.visible.add(table.name); 699 }} 700 for (const edge of edgeElements) {{ 701 edge.classList.toggle('hidden', !state.visible.has(edge.dataset.source) || !state.visible.has(edge.dataset.target)); 702 }} 703 for (const label of world.querySelectorAll('.group-label')) {{ 704 const anyVisible = hatchetOssModel.tables.some(table => table.source === label.dataset.source && state.visible.has(table.name)); 705 label.classList.toggle('hidden', !anyVisible); 706 }} 707 renderList(); 708 }} 709 710 function updateTransform() {{ 711 world.style.transform = `translate(${{state.x}}px, ${{state.y}}px) scale(${{state.scale}})`; 712 }} 713 714 function setZoom(next, anchorX = viewport.clientWidth / 2, anchorY = viewport.clientHeight / 2) {{ 715 const old = state.scale; 716 state.scale = Math.max(0.12, Math.min(1.8, next)); 717 state.x = anchorX - (anchorX - state.x) * state.scale / old; 718 state.y = anchorY - (anchorY - state.y) * state.scale / old; 719 updateTransform(); 720 }} 721 722 function fit() {{ 723 const visible = [...state.visible].map(name => positions.get(name)).filter(Boolean); 724 if (!visible.length) return; 725 const minX = Math.min(...visible.map(item => item.x)) - 25; 726 const minY = Math.min(...visible.map(item => item.y)) - 25; 727 const maxX = Math.max(...visible.map(item => item.x + item.width)) + 25; 728 const maxY = Math.max(...visible.map(item => item.y + item.height)) + 25; 729 state.scale = Math.max(0.12, Math.min(1.2, Math.min(viewport.clientWidth / (maxX - minX), viewport.clientHeight / (maxY - minY)))); 730 state.x = (viewport.clientWidth - (maxX - minX) * state.scale) / 2 - minX * state.scale; 731 state.y = (viewport.clientHeight - (maxY - minY) * state.scale) / 2 - minY * state.scale; 732 updateTransform(); 733 }} 734 735 function centerTable(name) {{ 736 const position = positions.get(name); 737 state.x = viewport.clientWidth / 2 - (position.x + position.width / 2) * state.scale; 738 state.y = viewport.clientHeight / 2 - (position.y + position.height / 2) * state.scale; 739 updateTransform(); 740 }} 741 742 let drag = null; 743 viewport.addEventListener('pointerdown', event => {{ 744 if (event.target.closest('.node')) return; 745 drag = {{ x: event.clientX, y: event.clientY, originX: state.x, originY: state.y }}; 746 viewport.setPointerCapture(event.pointerId); 747 viewport.classList.add('dragging'); 748 }}); 749 viewport.addEventListener('pointermove', event => {{ 750 if (!drag) return; 751 state.x = drag.originX + event.clientX - drag.x; 752 state.y = drag.originY + event.clientY - drag.y; 753 updateTransform(); 754 }}); 755 viewport.addEventListener('pointerup', () => {{ drag = null; viewport.classList.remove('dragging'); }}); 756 viewport.addEventListener('wheel', event => {{ 757 event.preventDefault(); 758 const rect = viewport.getBoundingClientRect(); 759 setZoom(state.scale * (event.deltaY > 0 ? 0.88 : 1.14), event.clientX - rect.left, event.clientY - rect.top); 760 }}, {{ passive: false }}); 761 viewport.addEventListener('click', () => selectTable(null, false)); 762 search.addEventListener('input', applyFilter); 763 sourceFilter.addEventListener('change', applyFilter); 764 document.getElementById('zoomIn').addEventListener('click', () => setZoom(state.scale * 1.2)); 765 document.getElementById('zoomOut').addEventListener('click', () => setZoom(state.scale / 1.2)); 766 document.getElementById('fit').addEventListener('click', fit); 767 768 layout(); 769 createNodes(); 770 createEdges(); 771 applyFilter(); 772 requestAnimationFrame(fit); 773 </script> 774 </body> 775 </html> 776 """ 777 -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /sdks/python/hatchet_sdk/config.py line 48
- Likely real
- Uncertain, 37% confidence
Possible binding to all interfaces.
47 ) 48 bind_address: str | None = "0.0.0.0" 49 -
- Engine
- GuardDog · Supply-chain attack heuristics
- Counts towards
- Malicious pattern
- File
- /cmd/hatchet-api/api/run.go line 18
- Likely real
- Yes, 93% confidence
Detects reading of environment variables (credential access, often contains secrets) [threat-runtime-environment-read]
traceIDRatio := os.Getenv("SERVER_OTEL_TRACE_ID_RATIO") collectorAuth := os.Getenv("SERVER_OTEL_COLLECTOR_AUTH") var insecureBool bool -
- Engine
- GuardDog · Supply-chain attack heuristics
- Counts towards
- Malicious pattern
- File
- /cmd/hatchet-cli/cli/embedded_ui_session.go line 40
- Likely real
- Yes, 93% confidence
Detects reading of environment variables (credential access, often contains secrets) [threat-runtime-environment-read]
email := os.Getenv("ADMIN_EMAIL") password := os.Getenv("ADMIN_PASSWORD") if email == "" && password == "" { -
- Engine
- GuardDog · Supply-chain attack heuristics
- Counts towards
- Malicious pattern
- File
- /cmd/hatchet-engine/engine/run.go line 55
- Likely real
- Yes, 93% confidence
Detects reading of environment variables (credential access, often contains secrets) [threat-runtime-environment-read]
traceIdRatio := os.Getenv("SERVER_OTEL_TRACE_ID_RATIO") collectorAuth := os.Getenv("SERVER_OTEL_COLLECTOR_AUTH") var insecureBool bool -
- Engine
- GuardDog · Supply-chain attack heuristics
- Counts towards
- Malicious pattern
- File
- /sdks/typescript/jest.e2e-global-setup.ts line 84
- Likely real
- Yes, 83% confidence
Detects fully silent process execution (suppressing all output channels) [threat-process-spawn-silent]
cwd: process.cwd(), detached: true, } ) as ChildProcess & { pid: number }; -
- Engine
- GuardDog · Supply-chain attack heuristics
- Counts towards
- Malicious pattern
- File
- /sdks/typescript/jest.e2e-global-setup.ts line 14
- Likely real
- Yes, 83% confidence
Detects fully silent process execution (suppressing all output channels) [threat-process-spawn-silent]
if (process.platform === 'win32') { execSync(`taskkill /pid ${pid} /T /F`, { stdio: 'ignore', timeout: killTimeoutMs }); } else { execSync(`pkill -TERM -P ${pid} 2>/dev/null; kill -TERM ${pid} 2>/dev... -
- Engine
- GuardDog · Supply-chain attack heuristics
- Counts towards
- Malicious pattern
- File
- /sdks/typescript/jest.e2e-global-setup.ts line 17
- Likely real
- Yes, 83% confidence
Detects fully silent process execution (suppressing all output channels) [threat-process-spawn-silent]
execSync(`pkill -TERM -P ${pid} 2>/dev/null; kill -TERM ${pid} 2>/dev/null; sleep 0.3; kill -9 -${pid} 2>/dev/null; kill -9 ${pid} 2>/dev/null`, { stdio: 'ignore', timeout: killTimeoutMs, }); -
- Engine
- GuardDog · Supply-chain attack heuristics
- Counts towards
- Malicious pattern
- File
- /sdks/typescript/package.json line 112
- Likely real
- Yes, 82% confidence
Detects LOLBAS usage in install hooks (execution and network tools) [threat-process-hooks]
"prepare": "pnpm run build", "postinstall": "node scripts/version-check.js", "dump-version": "node -e \"console.log('export const HATCHET_VERSION = \\'' + require('./packag... -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /sdks/python/hatchet_sdk/clients/rest/configuration.py line 511
- Likely real
- Uncertain, 45% confidence
Possible hardcoded password: ''
510 username = self.username 511 password = "" 512 if self.password is not None: -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /sdks/python/hatchet_sdk/worker/worker.py line 762
- Likely real
- Probably noise, 28% confidence
Try, Except, Continue detected.
761 queue.close() 762 except Exception: 763 continue 764 -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /sdks/python/hatchet_sdk/embedded.py line 7
- Likely real
- Probably noise, 13% confidence
Consider possible security implications associated with the subprocess module.
6 import platform 7 import subprocess 8 import sys -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /sdks/python/hatchet_sdk/embedded.py line 379
- Likely real
- Probably noise, 3% confidence
subprocess call - check for execution of untrusted input.
378 # the sidecar shuts down when its stdin closes, so it never outlives this process 379 process = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.DEVNULL) 380 atexit.register(process.terminate) -
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /sdks/python/hatchet_sdk/clients/listeners/pooled_listener.py
- Likely real
- Probably noise, 0% confidence
YARA rule matched: detect_downloader — Downloads a remote resource - common and low-signal alone; see detect_download_and_execute for the composite version
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /sdks/python/hatchet_sdk/embedded.py
- Likely real
- Probably noise, 0% confidence
YARA rule matched: detect_downloader — Downloads a remote resource - common and low-signal alone; see detect_download_and_execute for the composite version
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /hack/install/install.sh
- Likely real
- Probably noise, 0% confidence
YARA rule matched: detect_downloader — Downloads a remote resource - common and low-signal alone; see detect_download_and_execute for the composite version
-
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /sdks/python/hatchet_sdk/clients/rest/api_client.py line 284
- Likely real
- Probably noise, 0% confidence
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
283 msg = "RESTResponse.read() must be called before passing it to response_deserialize()" 284 assert response_data.data is not None, msg 285 -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /sdks/python/hatchet_sdk/clients/rest/api_client.py line 444
- Likely real
- Probably noise, 0% confidence
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
443 m = re.match(r"List\[(.*)]", klass) 444 assert m is not None, "Malformed List type definition" 445 sub_kls = m.group(1) -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /sdks/python/hatchet_sdk/clients/rest/api_client.py line 450
- Likely real
- Probably noise, 0% confidence
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
449 m = re.match(r"Dict\[([^,]*), (.*)]", klass) 450 assert m is not None, "Malformed Dict type definition" 451 sub_kls = m.group(2) -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /sdks/python/hatchet_sdk/clients/rest/api_client.py line 682
- Likely real
- Probably noise, 0% confidence
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
681 m = re.search(r'filename=[\'"]?([^\'"\s]+)[\'"]?', content_disposition) 682 assert m is not None, "Unexpected 'content-disposition' header value" 683 filename = m.group(1) -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /sdks/python/hatchet_sdk/clients/rest/rest.py line 144
- Likely real
- Probably noise, 0% confidence
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
143 method = method.upper() 144 assert method in ["GET", "HEAD", "DELETE", "POST", "PUT", "PATCH", "OPTIONS"] 145 -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /sdks/python/hatchet_sdk/connection.py line 21
- Likely real
- Probably noise, 0% confidence
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
20 elif config.tls_config.strategy == "mtls": 21 assert config.tls_config.root_ca_file 22 assert config.tls_config.key_file -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /sdks/python/hatchet_sdk/connection.py line 22
- Likely real
- Probably noise, 0% confidence
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
21 assert config.tls_config.root_ca_file 22 assert config.tls_config.key_file 23 assert config.tls_config.cert_file -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /sdks/python/hatchet_sdk/connection.py line 23
- Likely real
- Probably noise, 0% confidence
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
22 assert config.tls_config.key_file 23 assert config.tls_config.cert_file 24
No findings match these filters.