10 Commits
v1.6.0 ... main

Author SHA1 Message Date
AccentuSoft
16be1c5ee4 Bump python to 3.13, and app version 2025-02-06 14:43:30 +02:00
AccentuSoft
7346a42227 Move to using Fusion theme for app 2025-02-06 14:40:01 +02:00
AccentuSoft
85ed2e7227 Update syntax for EmailExtractor
Convert | to _

Another bug fix & bump version
2024-07-30 12:50:36 +03:00
AccentuSoft
f9f315da33 Bump version 2024-07-18 11:49:34 +03:00
AccentuSoft
741aa78ff4 Fix bug with SVG images lacking both 'width' and 'height' parameters 2024-07-18 11:46:16 +03:00
AccentuSoft
8e8838e6e9 Fix bugs & add more stuff to PATH 2024-07-04 19:37:30 +03:00
AccentuSoft
889c09ad14 Merge remote-tracking branch 'origin/main' 2024-06-28 21:25:29 +03:00
AccentuSoft
d11a7723f6 Small Cleanup 2024-06-26 18:14:58 +03:00
AccentuSoft
47051a66c2 Remove/Adjust console build option, and update version 2024-06-26 18:10:52 +03:00
AccentuSoft
61912960e2 Update README.md 2023-10-29 03:10:16 +02:00
16 changed files with 63 additions and 54 deletions

View File

@@ -1477,11 +1477,11 @@ class CanvasScene(QtWidgets.QGraphicsScene):
if groupItems is None:
newNode = Entity.BaseNode(picture, node, nodePrimaryAttribute, self.entityTextFont,
self.entityTextBrush)
self.addNodeToScene(newNode)
self.addNodeToScene(newNode, positions[node][0], positions[node][1])
else:
newNode = Entity.GroupNode(picture, node, nodePrimaryAttribute, self.entityTextFont,
self.entityTextBrush)
self.addNodeToScene(newNode)
self.addNodeToScene(newNode, positions[node][0], positions[node][1])
newGroupList = newNode.listWidget
newGroupListGraphic = self.addWidget(newGroupList)
@@ -1490,8 +1490,6 @@ class CanvasScene(QtWidgets.QGraphicsScene):
for item in groupItems:
self.sceneGraph.add_node(item, groupID=newNode.uid)
newNode.setPos(QtCore.QPointF(positions[node][0], positions[node][1]))
progressValue += 1
progress.setValue(progressValue)

View File

@@ -318,8 +318,8 @@ class BaseConnector(QGraphicsItemGroup):
def paint(self, painter: QtGui.QPainter, option: QtWidgets.QStyleOptionGraphicsItem,
widget: Optional[QtWidgets.QWidget] = ...) -> None:
currentStartPos = self.myStartItem.pos()
currentEndPos = self.myEndItem.pos()
currentStartPos = self.myStartItem.pos() - self.pos()
currentEndPos = self.myEndItem.pos() - self.pos()
self.myColor = self.colorSelected if self.isSelected() else self.colorDefault

View File

@@ -1189,7 +1189,7 @@ class MenuBar(QtWidgets.QMenuBar):
if itemJSONNotes != '':
itemPrimaryField = itemJSON[self.parent().RESOURCEHANDLER.getPrimaryFieldForEntityType(
itemJSON['Entity Type'])]
fileName = itemPrimaryField + ' | ' + itemJSON.get('Date Last Edited', str(time.time_ns())) + '.txt'
fileName = itemPrimaryField + '_' + itemJSON.get('Date Last Edited', str(time.time_ns())) + '.txt'
fileName = fileName.replace('/', '+')
fileName = fileName.replace('\\', '+')
with open(baseFilesPath / fileName, "w") as f:

View File

@@ -34,7 +34,7 @@ class ModulesManager:
self.browsersBaseDirectoryPath.mkdir(exist_ok=True)
self.modulesRequirementsPath = self.modulesBaseDirectoryPath / "requirements.txt"
self.modulesRequirementsTempPath = self.modulesBaseDirectoryPath / "requirements.txt.tmp"
self.modulesPythonPath = self.modulesBaseDirectoryPath / 'bin' / 'python3.11' if self.system == "Linux" \
self.modulesPythonPath = self.modulesBaseDirectoryPath / 'bin' / 'python3.13' if self.system == "Linux" \
else self.modulesBaseDirectoryPath / 'Scripts' / 'python.exe'
self.upgradeThread = None
self.moduleReqsThread = None
@@ -73,14 +73,13 @@ class ModulesManager:
def configureVenvLinux(self):
binDir = self.modulesBaseDirectoryPath / 'bin'
packagesPath = str(list((self.modulesBaseDirectoryPath / 'lib').glob('python*'))[0] / 'site-packages')
# prepend bin to PATH
os.environ["PATH"] = f'{binDir}{os.pathsep}{os.environ.get("PATH", "")}'
os.environ["PATH"] = f'{packagesPath}{os.pathsep}{binDir}{os.pathsep}{os.environ.get("PATH", "")}'
os.environ["VIRTUAL_ENV"] = str(self.modulesBaseDirectoryPath)
os.environ["PLAYWRIGHT_BROWSERS_PATH"] = str(self.browsersBaseDirectoryPath)
packagesPath = str(list((self.modulesBaseDirectoryPath / 'lib').glob('python*'))[0] / 'site-packages')
sys.path.append(packagesPath)
site.addsitedir(packagesPath)
@@ -88,17 +87,16 @@ class ModulesManager:
def configureVenvWindows(self):
binDir = self.modulesBaseDirectoryPath / 'Scripts'
packagesPath = self.modulesBaseDirectoryPath / 'Lib' / 'site-packages'
# prepend Scripts to PATH
os.environ["PATH"] = f'{binDir}{os.pathsep}{os.environ.get("PATH", "")}'
os.environ["PATH"] = f'{packagesPath}{os.pathsep}{binDir}{os.pathsep}{os.environ.get("PATH", "")}'
os.environ["VIRTUAL_ENV"] = str(self.modulesBaseDirectoryPath)
os.environ["PLAYWRIGHT_BROWSERS_PATH"] = str(self.browsersBaseDirectoryPath)
# add the virtual environments libraries to the host python import mechanism
packagesPath = str(self.modulesBaseDirectoryPath / 'Lib' / 'site-packages')
sys.path.append(packagesPath)
site.addsitedir(packagesPath)
sys.path.append(str(packagesPath))
site.addsitedir(str(packagesPath))
sys.prefix = str(self.modulesBaseDirectoryPath)
@@ -810,7 +808,7 @@ class InitialiseVenvThread(QtCore.QThread):
if self.modulesManager.system == "Linux" \
else self.modulesManager.modulesBaseDirectoryPath / 'Scripts'
if not binPath.exists():
pythonExecutable = "python3.11" if self.modulesManager.system == "Linux" else "python.exe"
pythonExecutable = "python3.13" if self.modulesManager.system == "Linux" else "python.exe"
cmdStr = (f'{pythonExecutable} -m venv --symlinks --clear --upgrade-deps '
f'"{self.modulesManager.modulesBaseDirectoryPath}"')
subprocess.run(cmdStr, shell=True)

View File

@@ -26,7 +26,6 @@ from Core.Interface.Entity import BaseNode
from Core.GlobalVariables import avoid_parsing_fields
class ReportWizard(QtWidgets.QWizard):
def __init__(self, parent):
super(ReportWizard, self).__init__(parent=parent)
@@ -332,17 +331,16 @@ class EntityPage(QtWidgets.QWizardPage):
self.reportWizard.parent().LENTDB.getEntity(self.entityUID)['Entity Type'])
contents = bytearray(self.defaultpic)
widthRegex = re.compile(b' width="\d*" ')
fileContents = ''
for widthMatches in widthRegex.findall(self.defaultpic):
fileContents = contents.replace(widthMatches, b' ')
contents = contents.replace(widthMatches, b' ')
heightRegex = re.compile(b' height="\d*" ')
for heightMatches in heightRegex.findall(self.defaultpic):
fileContents = contents.replace(heightMatches, b' ')
fileContents = fileContents.replace(b'<svg ', b'<svg height="150" width="150" ')
contents = contents.replace(heightMatches, b' ')
contents = contents.replace(b'<svg ', b'<svg height="150" width="150" ')
imagePath = Path(self.reportWizard.reportTempFolder) / f'{str(uuid4())}.svg'
with open(imagePath, 'wb') as tempFile:
tempFile.write(bytearray(fileContents))
tempFile.write(contents)
image = svg2rlg(imagePath)
data = {'EntityNotes': self.inputNotesEdit.toPlainText(), 'EntityImage': image}
@@ -385,7 +383,6 @@ class AppendixWidget(QtWidgets.QWidget):
self.inputAppendixImageEdit.setText(str(Path(selectedPath).absolute()))
class MyDocTemplate(BaseDocTemplate):
def __init__(self, filename, **kw):
self.allowSplitting = 0

View File

@@ -114,9 +114,9 @@ class EmailExtractor:
with contextlib.suppress(EmailNotValidError):
valid = validate_email(newLink[7:], dns_resolver=resolver,
check_deliverability=verifyDomain)
if valid.email not in allEmails:
allEmails.add(valid.email)
returnResults.append([{'Email Address': valid.email,
if valid.normalized not in allEmails:
allEmails.add(valid.normalized)
returnResults.append([{'Email Address': valid.normalized,
'Entity Type': 'Email Address'},
{currentUID: {'Resolution': 'Email Address Found',
'Notes': ''}}])

View File

@@ -118,7 +118,7 @@ class FileExtractor:
'Notes': ''}}])
docProperName = link.split('/')[-1]
docFileName = f'{md5(link.encode("UTF-8")).hexdigest()} | {docProperName}'
docFileName = f'{md5(link.encode("UTF-8")).hexdigest()}_{docProperName}'
docFullPath = Path(parameters['Project Files Directory']) / docFileName
with contextlib.suppress(Exception):
@@ -157,7 +157,7 @@ class FileExtractor:
{uid: {'Resolution': 'File URL',
'Notes': ''}}])
docProperName = link.split('/')[-1]
docFileName = f'{md5(link.encode()).hexdigest()} | {docProperName}'
docFileName = f'{md5(link.encode()).hexdigest()}_{docProperName}'
docFullPath = Path(parameters['Project Files Directory']) / docFileName
with contextlib.suppress(Exception):

View File

@@ -16,8 +16,7 @@ class HostnameToDomain:
for entity in entityJsonList:
uid = entity['uid']
primary_field = entity[list(entity)[1]].strip()
tsd, td, tsu = extract(primary_field)
domain = f'{td}.{tsu}'
domain = extract(primary_field).fqdn
if domain == primary_field:
continue
return_result.append([{'Domain Name': domain,

View File

@@ -23,7 +23,7 @@ class SettingsObject(dict):
def __init__(self):
super().__init__()
self.globalSettings = QSettings()
self.globalSettings.setValue("Program/Version", "v1.6.0")
self.globalSettings.setValue("Program/Version", "v1.6.5")
self.globalSettings.setValue("Program/TOR Profile Location",
self.globalSettings.value("Program/TOR Profile Location", ""))
self.globalSettings.setValue("Program/BaseDir",

View File

@@ -90,7 +90,7 @@ class URLManager:
projectFilesPath = Path(self.mainWindow.SETTINGS.value("Project/FilesDir"))
# Create a unique path in Project Files
saveHash = sha3_512(str(urlPath).encode()).hexdigest()[:16] # nosec
savePath = projectFilesPath / f'{saveHash}|{urlPath.name}'
savePath = projectFilesPath / f'{saveHash}_{urlPath.name}'
if createSymlink:
symlink(urlPath, savePath)

View File

@@ -7,6 +7,7 @@ import os
import platform
import subprocess
import tempfile
import stat
from pathlib import Path
import requests
@@ -22,8 +23,8 @@ from PySide6 import QtCore, QtWidgets, QtGui
"""
python -m nuitka --follow-imports --onefile --noinclude-pytest-mode=nofollow --noinclude-setuptools-mode=nofollow ^
--noinclude-custom-mode=setuptools:error --noinclude-IPython-mode=nofollow --enable-plugin=pyside6 ^
--assume-yes-for-downloads --remove-output --disable-console --warn-unusual-code --show-modules ^
--windows-company-name="AccentuSoft" --windows-product-name="LinkScope Installer" --windows-product-version=1.6.0.0 ^
--assume-yes-for-downloads --remove-output --windows-console-mode=disable --warn-unusual-code --show-modules ^
--windows-company-name="AccentuSoft" --windows-product-name="LinkScope Installer" --windows-product-version=1.6.5.0 ^
--include-data-files="Icon.ico=Icon.ico" --windows-icon-from-ico=".\Icon.ico" ^
--windows-file-description="LinkScope Installer" ^
Installer.py
@@ -33,7 +34,7 @@ Installer.py
"""
python -m nuitka --follow-imports --onefile --noinclude-pytest-mode=nofollow --noinclude-setuptools-mode=nofollow \
--noinclude-custom-mode=setuptools:error --noinclude-IPython-mode=nofollow --enable-plugin=pyside6 \
--assume-yes-for-downloads --remove-output --disable-console --warn-unusual-code --show-modules \
--assume-yes-for-downloads --remove-output --warn-unusual-code --show-modules \
--include-data-files="Icon.ico=Icon.ico" --linux-icon="Icon.ico" \
Installer.py
"""
@@ -715,10 +716,24 @@ For more information on this, and how to apply and follow the GNU AGPL, see
"""
def recursiveMakeWriteableHelper(targetPath: Path):
for root, dirs, files in os.walk(targetPath):
for dir_name in dirs:
dir_path = os.path.join(root, dir_name)
os.chmod(dir_path, stat.S_IWRITE)
if not os.access(dir_path, os.W_OK):
os.chmod(dir_path, stat.S_IWRITE | stat.S_IREAD | stat.S_IEXEC)
for file_name in files:
file_path = os.path.join(root, file_name)
os.chmod(file_path, stat.S_IWRITE)
if not os.access(file_path, os.W_OK):
os.chmod(file_path, stat.S_IWRITE | stat.S_IREAD)
def deleteVenvStuff() -> None:
baseAppStoragePath = Path(
QtCore.QStandardPaths.standardLocations(
QtCore.QStandardPaths.StandardLocation.AppDataLocation)[0])
recursiveMakeWriteableHelper(baseAppStoragePath)
shutil.rmtree(baseAppStoragePath)
@@ -764,8 +779,8 @@ def installGraphvizWindowsHelper():
def installPythonLinuxHelper():
subprocess.run('echo "y" | add-apt-repository ppa:deadsnakes/ppa', shell=True)
subprocess.run('apt-get update && apt-get install python3.11 python3.11-venv -y', shell=True)
subprocess.run('wget -O - https://bootstrap.pypa.io/get-pip.py | python3.11', shell=True)
subprocess.run('apt-get update && apt-get install python3.13 python3.13-venv -y', shell=True)
subprocess.run('wget -O - https://bootstrap.pypa.io/get-pip.py | python3.13', shell=True)
def installPythonWindowsHelper():
@@ -775,7 +790,7 @@ def installPythonWindowsHelper():
return False
latest_release_path = win_downloads.text.split(
'Latest Python 3 Release -', 1)[0].split('href="')[-1].split('"', 1)[0]
'Python 3.13.', 1)[0].split('href="')[-1].split('"', 1)[0]
latest_release_full_path = f'https://www.python.org{latest_release_path}'
@@ -805,8 +820,10 @@ def removeFileHelper(pathToRemove: Path):
if not pathToRemove.exists():
return
if pathToRemove.is_dir():
recursiveMakeWriteableHelper(pathToRemove)
shutil.rmtree(pathToRemove)
else:
os.chmod(pathToRemove, stat.S_IWRITE)
pathToRemove.unlink(missing_ok=True)
@@ -1014,7 +1031,7 @@ class InstallWizard(QtWidgets.QWizard):
if self.currentOS == 'Linux':
try:
if subprocess.run(["python3.11", "--version"],
if subprocess.run(["python3.13", "--version"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True).returncode != 0:
@@ -1154,9 +1171,9 @@ class LinkScopeInstallLatestPage(QtWidgets.QWizardPage):
class LinkScopeUninstallPage(QtWidgets.QWizardPage):
def doStuff(self):
deleteVenvStuff()
self.progressBar.setValue(3)
try:
deleteVenvStuff()
self.progressBar.setValue(3)
self.wizard().uninstall()
except Exception as e:
self.wizard().page(6).doneLabel.setText('Error occurred during uninstallation: ' +
@@ -1315,5 +1332,7 @@ class InstallThread(QtCore.QThread):
if __name__ == '__main__':
application = QtWidgets.QApplication(sys.argv)
application.setOrganizationName("AccentuSoft")
application.setApplicationName("LinkScope Client")
installWizard = InstallWizard()
sys.exit(application.exec())

View File

@@ -10,7 +10,6 @@ import itertools
import threading
import networkx as nx
import qdarktheme
from ast import literal_eval
from uuid import uuid4
from shutil import move
@@ -3251,6 +3250,6 @@ if __name__ == '__main__':
application = QtWidgets.QApplication(sys.argv)
application.setOrganizationName("AccentuSoft")
application.setApplicationName("LinkScope Client")
qdarktheme.setup_theme()
application.setStyle('fusion')
mainWindow = MainWindow()
sys.exit(application.exec())

View File

@@ -42,7 +42,7 @@ Note that the SFDP graph layout does not function on Windows, as an essential gr
Since Version 1.0.0, installers are provided for Windows 11 and Linux (Ubuntu) platforms.
Download the latest installer for your platform from the Releases page, and run it to install the software:
https://github.com/AccentuSoft/LinkScope_Client/releases/tag/v1.5.0
https://github.com/AccentuSoft/LinkScope_Client/releases/tag/v1.6.5
Note: On Ubuntu, you may need to mark the downloaded installer as executable before you can run it. To do this, right-click the installer, and from the drop-down menu, select 'properties'. On the dialog window that pops up, navigate to the 'Permissions' tab, and make sure that 'Allow executing file as program' is checked. You should at this point be able to run the installer by double-clicking it. If double-clicking the installer does not start it, you can also launch the installer through a terminal.

View File

@@ -4,7 +4,7 @@
# to a new directory along with this file.
# Then, run the script.
PYTHON_VER=3.11
PYTHON_VER=3.13
python${PYTHON_VER} -m venv buildEnv
@@ -21,11 +21,11 @@ python${PYTHON_VER} -m nuitka --follow-imports --standalone --noinclude-pytest-m
--noinclude-setuptools-mode=nofollow --noinclude-custom-mode=setuptools:error --noinclude-IPython-mode=nofollow \
--noinclude-unittest-mode=nofollow --enable-plugin=pyside6 --assume-yes-for-downloads --remove-output \
--nofollow-import-to=tkinter \
--disable-console --include-data-dir="Resources=Resources" --include-package=Core \
--include-data-dir="Resources=Resources" --include-package=Core \
--include-data-dir="Core/Entities=Core/Entities" \
--warn-unusual-code --show-modules --include-data-files="Icon.ico=Icon.ico" \
--linux-icon="Icon.ico" \
--include-package-data=playwright \
--include-package-data=requests \
--include-package-data=folium --include-package-data=branca \
--include-package=dns \
--include-package-data=pycountry \

View File

@@ -8,7 +8,7 @@
:: UpdaterUtil.py and PatchMagicWin.py to a new directory along with this file.
:: Then, run the script.
SET PYTHON_VER=3.11
SET PYTHON_VER=3.13
python -m venv buildEnv
@@ -25,15 +25,15 @@ python PatchMagicWin.py
python -m nuitka --follow-imports --standalone --noinclude-pytest-mode=nofollow --noinclude-setuptools-mode=nofollow ^
--noinclude-custom-mode=setuptools:error --noinclude-IPython-mode=nofollow --enable-plugin=pyside6 ^
--noinclude-unittest-mode=nofollow --assume-yes-for-downloads --remove-output --disable-console ^
--noinclude-unittest-mode=nofollow --assume-yes-for-downloads --remove-output --windows-console-mode=disable ^
--nofollow-import-to=tkinter ^
--include-data-dir="Resources=Resources" --include-package=Core ^
--include-data-dir="Core\Entities=Core\Entities" ^
--warn-unusual-code --show-modules --include-data-files="Icon.ico=Icon.ico" ^
--windows-icon-from-ico=".\Icon.ico" --include-data-dir="magic=magic" --windows-company-name=AccentuSoft ^
--windows-product-name="LinkScope Client" --windows-product-version="1.6.0.0" ^
--windows-product-name="LinkScope Client" --windows-product-version="1.6.5.0" ^
--windows-file-description="LinkScope Client Software" ^
--include-package-data=playwright ^
--include-package-data=requests ^
--include-package-data=folium --include-package-data=branca ^
--include-package=dns ^
--include-package-data=pycountry ^
@@ -46,7 +46,7 @@ python -m nuitka --onefile --noinclude-pytest-mode=nofollow ^
--noinclude-unittest-mode=nofollow --assume-yes-for-downloads --remove-output ^
--nofollow-import-to=tkinter ^
--warn-unusual-code ^
--windows-company-name=AccentuSoft --windows-product-version="1.6.0.0" ^
--windows-company-name=AccentuSoft --windows-product-version="1.6.5.0" ^
--windows-product-name="LinkScope Client Updater" --windows-file-description="LinkScope Client Updater" ^
".\UpdaterUtil.py"

View File

@@ -1,5 +1,4 @@
PySide6
pyqtdarktheme
# For python-magic, the following packages may be required: