Skip to content

Commit 79f3987

Browse files
committed
Addressing warnings.
1 parent 5f515fa commit 79f3987

File tree

14 files changed

+125
-52
lines changed

14 files changed

+125
-52
lines changed

.github/dependabot.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ updates:
1010
reviewers:
1111
- "tlbauer"
1212
assignees:
13-
- "tlbauer"
13+
- "tlbauer2"
1414
commit-message:
1515
prefix: "deps"
1616
include: "scope"
@@ -40,7 +40,7 @@ updates:
4040
reviewers:
4141
- "tlbauer"
4242
assignees:
43-
- "tlbauer"
43+
- "tlbauer2"
4444
commit-message:
4545
prefix: "docker"
4646
include: "scope"

.github/workflows/ci-cd.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,12 @@ jobs:
7474
- name: Run Bandit security scan
7575
run: |
7676
bandit -r src/ -f json -o bandit-report.json || true
77-
bandit -r src/ -f txt || true # TODO: Revisit - consider failing build on security issues
77+
bandit -r src/ -f txt
7878
7979
- name: Run Safety dependency scan
8080
run: |
8181
safety check --json --output safety-report.json || true
82-
safety check || true # TODO: Revisit - consider failing build on dependency vulnerabilities
82+
safety check
8383
8484
- name: Upload security scan results
8585
uses: actions/upload-artifact@v4

.safety-project.ini

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[project]
2+
id = talkpipe
3+
url = /codebases/talkpipe/findings
4+
name = talkpipe
5+

src/talkpipe/app/chatterlang_reference_browser.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -352,20 +352,20 @@ def main():
352352
print("Attempting to run 'chatterlang_reference_generator' command to generate them...")
353353

354354
try:
355-
import subprocess
356-
subprocess.run(['chatterlang_reference_generator'], capture_output=True, text=True, check=True)
355+
from talkpipe.app.chatterlang_reference_generator import go as generate_reference
356+
generate_reference()
357357
print("Successfully generated reference files.")
358358

359359
# Check if files were created
360360
if txt_file.exists():
361361
doc_path = str(current_dir)
362362
else:
363-
print("Error: talkpipe_ref command completed but files were not created")
363+
print("Error: reference generation completed but files were not created")
364364
sys.exit(1)
365-
except (subprocess.CalledProcessError, FileNotFoundError) as e:
366-
print(f"Error running talkpipe_ref command: {e}")
365+
except Exception as e:
366+
print(f"Error running reference generator: {e}")
367367
print("Please either:")
368-
print(" 1. Install TalkPipe and ensure 'talkpipe_ref' is in PATH, or")
368+
print(" 1. Install TalkPipe properly, or")
369369
print(" 2. Provide path to directory containing talkpipe_ref.txt")
370370
sys.exit(1)
371371
else:

src/talkpipe/app/chatterlang_serve.py

Lines changed: 62 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,14 @@ def add_output(self, output: str, message_type: str = "response"):
7676
"type": message_type
7777
}
7878
self.output_queue.put(timestamped_output, block=False)
79-
except:
79+
except Exception as e:
8080
# Queue is full, remove oldest item
81+
logger.warning(f"Output queue full, attempting to remove oldest item: {e}")
8182
try:
8283
self.output_queue.get_nowait()
8384
self.output_queue.put(timestamped_output, block=False)
84-
except:
85-
pass
85+
except Exception as e2:
86+
logger.warning(f"Failed to add output to queue even after removing oldest item: {e2}")
8687

8788
def update_activity(self):
8889
"""Update last activity timestamp"""
@@ -125,7 +126,7 @@ class ChatterlangServer:
125126

126127
def __init__(
127128
self,
128-
host: str = "0.0.0.0",
129+
host: str = "localhost",
129130
port: int = 9999,
130131
api_key: str = "your-secret-key-here",
131132
require_auth: bool = False,
@@ -637,11 +638,11 @@ def _get_stream_interface(self) -> str:
637638

638639
form_fields = self._generate_form_fields()
639640

640-
return f'''
641+
template = '''
641642
<!DOCTYPE html>
642643
<html>
643644
<head>
644-
<title>{self.title} - Stream</title>
645+
<title>{title} - Stream</title>
645646
<style>
646647
* {{
647648
margin: 0;
@@ -983,7 +984,7 @@ def _get_stream_interface(self) -> str:
983984
</head>
984985
<body>
985986
<div class="header">
986-
<h1>{self.form_config.title}</h1>
987+
<h1>{form_config_title}</h1>
987988
</div>
988989
989990
<div class="main-container">
@@ -1169,7 +1170,7 @@ def _get_stream_interface(self) -> str:
11691170
}}
11701171
11711172
// Add user message to chat immediately for instant feedback
1172-
const displayProperty = '{self.display_property}' || Object.keys(data)[0];
1173+
const displayProperty = '{display_property}' || Object.keys(data)[0];
11731174
const userMessage = data[displayProperty] || JSON.stringify(data);
11741175
lastUserMessage = userMessage; // Store to detect duplicates from server
11751176
addMessage(userMessage, 'user', new Date().toISOString());
@@ -1228,6 +1229,34 @@ def _get_stream_interface(self) -> str:
12281229
</body>
12291230
</html>
12301231
'''
1232+
return template.format(
1233+
title=self.title,
1234+
height=height,
1235+
form_style=form_style,
1236+
input_bg=input_bg,
1237+
text_color=text_color,
1238+
border_color=border_color,
1239+
button_bg=button_bg,
1240+
button_hover=button_hover,
1241+
form_fields=form_fields,
1242+
auth_header=auth_header,
1243+
form_config_title=self.form_config.title,
1244+
bg_color=bg_color,
1245+
output_bg=output_bg,
1246+
main_container_style=main_container_style,
1247+
form_panel_width=form_panel_width,
1248+
form_panel_style=form_panel_style,
1249+
chat_panel_style=chat_panel_style,
1250+
user_msg_bg=user_msg_bg,
1251+
response_msg_bg=response_msg_bg,
1252+
output_text=output_text,
1253+
error_msg_bg=error_msg_bg,
1254+
controls_style=controls_style,
1255+
form_panel_class=form_panel_class,
1256+
chat_controls=chat_controls,
1257+
standalone_controls=standalone_controls,
1258+
display_property=self.display_property
1259+
)
12311260

12321261
def _get_html_interface(self) -> str:
12331262
"""Generate HTML interface with configurable form"""
@@ -1270,11 +1299,11 @@ def _get_html_interface(self) -> str:
12701299

12711300
form_fields = self._generate_form_fields()
12721301

1273-
return f'''
1302+
template2 = '''
12741303
<!DOCTYPE html>
12751304
<html>
12761305
<head>
1277-
<title>{self.title}</title>
1306+
<title>{title}</title>
12781307
<style>
12791308
* {{
12801309
margin: 0;
@@ -1483,10 +1512,10 @@ def _get_html_interface(self) -> str:
14831512
<body>
14841513
<div class="main-content">
14851514
<div class="info-section">
1486-
<h1>{self.title}</h1>
1515+
<h1>{title}</h1>
14871516
<p>Submit JSON data using the form below or send POST requests to:</p>
1488-
<div class="endpoint-info">POST http://{self.host}:{self.port}/process</div>
1489-
{"<p>Authentication required: Include 'X-API-Key' header</p>" if self.require_auth else ""}
1517+
<div class="endpoint-info">POST http://{host}:{port}/process</div>
1518+
{require_auth_text}
14901519
<p>View API documentation at: <a href="/docs">/docs</a></p>
14911520
<p>View streaming interface at: <a href="/stream">/stream</a></p>
14921521
</div>
@@ -1502,7 +1531,7 @@ def _get_html_interface(self) -> str:
15021531
<div class="form-panel" id="formPanel">
15031532
<div class="form-container">
15041533
<div class="form-header">
1505-
<h3>{self.form_config.title}</h3>
1534+
<h3>{form_config_title}</h3>
15061535
</div>
15071536
15081537
{auth_header}
@@ -1643,6 +1672,22 @@ def _get_html_interface(self) -> str:
16431672
</body>
16441673
</html>
16451674
'''
1675+
return template2.format(
1676+
title=self.title,
1677+
height=height,
1678+
form_style=form_style,
1679+
input_bg=input_bg,
1680+
text_color=text_color,
1681+
border_color=border_color,
1682+
button_bg=button_bg,
1683+
button_hover=button_hover,
1684+
form_fields=form_fields,
1685+
auth_header=auth_header,
1686+
form_config_title=self.form_config.title,
1687+
host=self.host,
1688+
port=self.port,
1689+
require_auth_text='<p>Authentication required: Include "X-API-Key" header</p>' if self.require_auth else ''
1690+
)
16461691

16471692
def set_processor_function(self, func: Callable[[Dict[str, Any]], Any]):
16481693
"""Set the function used to process incoming JSON data"""
@@ -1696,7 +1741,7 @@ def load_form_config(config_path: str) -> Dict[str, Any]:
16961741
class ChatterlangServerSegment(AbstractSource):
16971742
"""Segment for receiving JSON data via FastAPI with configurable form"""
16981743

1699-
def __init__(self, port: Union[int,str] = 9999, host: str = "0.0.0.0",
1744+
def __init__(self, port: Union[int,str] = 9999, host: str = "localhost",
17001745
api_key: str = None, require_auth: bool = False,
17011746
form_config: Union[str, Dict[str, Any]] = None):
17021747
super().__init__()
@@ -1776,8 +1821,8 @@ def go():
17761821
parser = argparse.ArgumentParser(description='FastAPI JSON Data Receiver with Configurable Form')
17771822
parser.add_argument('-p', '--port', type=int, default=2025,
17781823
help='Port to listen on (default: 2025)')
1779-
parser.add_argument('-o', '--host', default='0.0.0.0',
1780-
help='Host to bind to (default: 0.0.0.0)')
1824+
parser.add_argument('-o', '--host', default='localhost',
1825+
help='Host to bind to (default: localhost)')
17811826
parser.add_argument('--api-key', help='Set API key for authentication')
17821827
parser.add_argument('--require-auth', action='store_true',
17831828
help='Require API key authentication')

src/talkpipe/data/email.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,10 @@ def sendEmail(items, subject_field, body_fields, sender_email, recipient_email,
173173
- Uses TLS encryption for email transmission
174174
"""
175175
logger.debug(f"Starting sendEmail with subject_field={subject_field}, body_fields={body_fields}")
176-
assert subject_field is not None, "subject_field is required"
177-
assert body_fields is not None, "body_fields is required"
176+
if subject_field is None:
177+
raise ValueError("subject_field is required")
178+
if body_fields is None:
179+
raise ValueError("body_fields is required")
178180

179181
config = get_config()
180182
logger.debug(f"Loaded config: {config}")
@@ -347,7 +349,8 @@ def fetch_emails(
347349

348350
try:
349351
date_obj = email.utils.parsedate_to_datetime(date_str)
350-
except:
352+
except Exception as e:
353+
logger.warning(f"Failed to parse email date '{date_str}': {e}. Using current time.")
351354
date_obj = datetime.datetime.now()
352355

353356
# Extract content

src/talkpipe/data/html.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -98,26 +98,34 @@ def htmlToTextSegment(raw, cleanText=True):
9898
def get_robot_parser(domain, timeout=5):
9999
"""Retrieve or create a RobotFileParser for a given domain with a timeout."""
100100
robots_url = f"{domain}/robots.txt"
101+
102+
# Validate URL scheme for security
103+
parsed_url = urllib.parse.urlparse(robots_url)
104+
if parsed_url.scheme not in ('http', 'https'):
105+
logger.warning(f"Unsafe URL scheme '{parsed_url.scheme}' in robots_url: {robots_url}. Only http/https allowed.")
106+
return None
107+
101108
rp = RobotFileParser()
102109
rp.set_url(robots_url)
103110

104111
try:
105-
with urllib.request.urlopen(robots_url, timeout=timeout) as response:
106-
content = response.read()
107-
if content.startswith(b'\x1f\x8b'):
108-
# If the content is gzipped, decompress it
109-
content = gzip.decompress(content).decode('utf-8')
110-
else:
111-
# If not gzipped, decode it directly
112-
content = content.decode('utf-8')
112+
response = requests.get(robots_url, timeout=timeout)
113+
response.raise_for_status() # Raise an exception for bad status codes
114+
content_bytes = response.content
115+
if content_bytes.startswith(b'\x1f\x8b'):
116+
# If the content is gzipped, decompress it
117+
content = gzip.decompress(content_bytes).decode('utf-8')
118+
else:
119+
# If not gzipped, decode it directly
120+
content = content_bytes.decode('utf-8')
113121

114122
try:
115123
rp.parse(content.splitlines())
116124
except Exception as e:
117125
# If parsing fails, log the error but return the robot parser anyway
118126
logger.warning(f"Error parsing robots.txt from {robots_url}: {e}")
119127

120-
except (urllib.error.URLError, ConnectionError, TimeoutError) as e:
128+
except (requests.RequestException, ConnectionError, TimeoutError) as e:
121129
logger.warning(f"Failed to fetch robots.txt from {robots_url}. Assuming allowed. Error: {e}")
122130
return None # Use None to indicate failure to fetch
123131

src/talkpipe/operations/filtering.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ def _hashes(self, item):
4040
# Convert the item to bytes (if it's not already) so it can be hashed.
4141
item_bytes = str(item).encode('utf-8')
4242

43-
# First hash using MD5
44-
hash1 = int(hashlib.md5(item_bytes).hexdigest(), 16)
45-
# Second hash using SHA-1
46-
hash2 = int(hashlib.sha1(item_bytes).hexdigest(), 16)
43+
# First hash using MD5 (not for security, used for bloom filter hashing)
44+
hash1 = int(hashlib.md5(item_bytes, usedforsecurity=False).hexdigest(), 16)
45+
# Second hash using SHA-1 (not for security, used for bloom filter hashing)
46+
hash2 = int(hashlib.sha1(item_bytes, usedforsecurity=False).hexdigest(), 16)
4747

4848
# Generate hash values using double hashing:
4949
# For each i, compute: (hash1 + i * hash2) mod size

src/talkpipe/operations/transforms.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ def fill_null(items, default='', **kwargs):
8484
[{'a': None, 'b': 1}, {'a': 2, 'b': 'EMPTY'}]
8585
"""
8686
for item in items:
87-
assert isinstance(item, dict), f"Expected a dictionary, but got {type(item)} instead"
87+
if not isinstance(item, dict):
88+
raise TypeError(f"Expected a dictionary, but got {type(item)} instead")
8889
for k in item:
8990
if item[k] is None and k not in kwargs:
9091
item[k] = default
@@ -129,7 +130,8 @@ def transform(self, input_iter):
129130
130131
"""
131132

132-
assert self.num_items is None or self.num_items > 0, "num_vectors must be None or a positive integer"
133+
if self.num_items is not None and self.num_items <= 0:
134+
raise ValueError("num_vectors must be None or a positive integer")
133135
accumulated = []
134136
for datum in input_iter:
135137
item = extract_property(datum, self.field)

src/talkpipe/pipe/basic.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,8 @@ def fillTemplate(item, template:str, fail_on_missing:bool=True, default: Optiona
555555
Returns:
556556
str: The filled template string
557557
"""
558-
assert template is not None
558+
if template is None:
559+
raise ValueError("Template cannot be None")
559560
field_names = extract_template_field_names(template)
560561
values = {field_name:extract_property(item, field_name, fail_on_missing=fail_on_missing, default=default) for field_name in field_names}
561562
return fill_template(template, values)

0 commit comments

Comments
 (0)