Compare commits
No commits in common. "fbaed1bac3914fa706eb45230d6163aa74532e80" and "d7a336e524c007ca3153f3b9e081254e34458fe2" have entirely different histories.
fbaed1bac3
...
d7a336e524
@ -1,5 +1,5 @@
|
|||||||
project('roster',
|
project('roster',
|
||||||
version: '0.6.0',
|
version: '0.5.0',
|
||||||
meson_version: '>= 1.0.0',
|
meson_version: '>= 1.0.0',
|
||||||
default_options: [ 'warning_level=2', 'werror=false', ],
|
default_options: [ 'warning_level=2', 'werror=false', ],
|
||||||
)
|
)
|
||||||
|
|||||||
@ -80,7 +80,6 @@ class HistoryEntry:
|
|||||||
response: Optional[HttpResponse]
|
response: Optional[HttpResponse]
|
||||||
error: Optional[str] # Error message if request failed
|
error: Optional[str] # Error message if request failed
|
||||||
id: str = None # Unique identifier for the entry
|
id: str = None # Unique identifier for the entry
|
||||||
has_redacted_variables: bool = False # True if sensitive variables were redacted
|
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
"""Generate UUID if id not provided."""
|
"""Generate UUID if id not provided."""
|
||||||
@ -95,8 +94,7 @@ class HistoryEntry:
|
|||||||
'timestamp': self.timestamp,
|
'timestamp': self.timestamp,
|
||||||
'request': self.request.to_dict(),
|
'request': self.request.to_dict(),
|
||||||
'response': self.response.to_dict() if self.response else None,
|
'response': self.response.to_dict() if self.response else None,
|
||||||
'error': self.error,
|
'error': self.error
|
||||||
'has_redacted_variables': self.has_redacted_variables
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -107,8 +105,7 @@ class HistoryEntry:
|
|||||||
timestamp=data['timestamp'],
|
timestamp=data['timestamp'],
|
||||||
request=HttpRequest.from_dict(data['request']),
|
request=HttpRequest.from_dict(data['request']),
|
||||||
response=HttpResponse.from_dict(data['response']) if data.get('response') else None,
|
response=HttpResponse.from_dict(data['response']) if data.get('response') else None,
|
||||||
error=data.get('error'),
|
error=data.get('error')
|
||||||
has_redacted_variables=data.get('has_redacted_variables', False)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -127,118 +127,3 @@ class VariableSubstitution:
|
|||||||
)
|
)
|
||||||
|
|
||||||
return new_request, all_undefined
|
return new_request, all_undefined
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def substitute_excluding_variables(text: str, variables: Dict[str, str], excluded_vars: List[str]) -> Tuple[str, List[str]]:
|
|
||||||
"""
|
|
||||||
Replace {{variable_name}} with values, but skip excluded variables (leave as {{var}}).
|
|
||||||
|
|
||||||
Args:
|
|
||||||
text: The text containing variable placeholders
|
|
||||||
variables: Dictionary mapping variable names to their values
|
|
||||||
excluded_vars: List of variable names to skip (leave as placeholders)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Tuple of (substituted_text, list_of_undefined_variables)
|
|
||||||
"""
|
|
||||||
if not text:
|
|
||||||
return text, []
|
|
||||||
|
|
||||||
undefined_vars = []
|
|
||||||
|
|
||||||
def replace_var(match):
|
|
||||||
var_name = match.group(1)
|
|
||||||
|
|
||||||
# Skip excluded variables - leave as {{var_name}}
|
|
||||||
if var_name in excluded_vars:
|
|
||||||
return match.group(0) # Return original {{var_name}}
|
|
||||||
|
|
||||||
# Substitute non-excluded variables
|
|
||||||
if var_name in variables:
|
|
||||||
value = variables[var_name]
|
|
||||||
# Check if value is empty/None - treat as undefined for warning purposes
|
|
||||||
if not value:
|
|
||||||
undefined_vars.append(var_name)
|
|
||||||
return ""
|
|
||||||
return value
|
|
||||||
else:
|
|
||||||
# Variable not defined - track it and replace with empty string
|
|
||||||
undefined_vars.append(var_name)
|
|
||||||
return ""
|
|
||||||
|
|
||||||
substituted = VariableSubstitution.VARIABLE_PATTERN.sub(replace_var, text)
|
|
||||||
return substituted, undefined_vars
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def substitute_request_excluding_sensitive(
|
|
||||||
request: HttpRequest,
|
|
||||||
environment: Environment,
|
|
||||||
sensitive_variables: List[str]
|
|
||||||
) -> Tuple[HttpRequest, Set[str], bool]:
|
|
||||||
"""
|
|
||||||
Substitute variables in request, but exclude sensitive variables (leave as {{var}}).
|
|
||||||
|
|
||||||
This is useful for saving requests to history without exposing sensitive values.
|
|
||||||
Sensitive variables remain in their {{variable_name}} placeholder form.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request: The HTTP request with variable placeholders
|
|
||||||
environment: The environment containing variable values
|
|
||||||
sensitive_variables: List of variable names to exclude from substitution
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Tuple of (redacted_request, set_of_undefined_variables, has_sensitive_vars)
|
|
||||||
- redacted_request: Request with only non-sensitive variables substituted
|
|
||||||
- set_of_undefined_variables: Variables that were referenced but not defined
|
|
||||||
- has_sensitive_vars: True if any sensitive variables were found and redacted
|
|
||||||
"""
|
|
||||||
all_undefined = set()
|
|
||||||
|
|
||||||
# Substitute URL (excluding sensitive variables)
|
|
||||||
new_url, url_undefined = VariableSubstitution.substitute_excluding_variables(
|
|
||||||
request.url, environment.variables, sensitive_variables
|
|
||||||
)
|
|
||||||
all_undefined.update(url_undefined)
|
|
||||||
|
|
||||||
# Substitute headers (both keys and values, excluding sensitive variables)
|
|
||||||
new_headers = {}
|
|
||||||
for key, value in request.headers.items():
|
|
||||||
new_key, key_undefined = VariableSubstitution.substitute_excluding_variables(
|
|
||||||
key, environment.variables, sensitive_variables
|
|
||||||
)
|
|
||||||
new_value, value_undefined = VariableSubstitution.substitute_excluding_variables(
|
|
||||||
value, environment.variables, sensitive_variables
|
|
||||||
)
|
|
||||||
all_undefined.update(key_undefined)
|
|
||||||
all_undefined.update(value_undefined)
|
|
||||||
new_headers[new_key] = new_value
|
|
||||||
|
|
||||||
# Substitute body (excluding sensitive variables)
|
|
||||||
new_body, body_undefined = VariableSubstitution.substitute_excluding_variables(
|
|
||||||
request.body, environment.variables, sensitive_variables
|
|
||||||
)
|
|
||||||
all_undefined.update(body_undefined)
|
|
||||||
|
|
||||||
# Create new HttpRequest with redacted values
|
|
||||||
redacted_request = HttpRequest(
|
|
||||||
method=request.method,
|
|
||||||
url=new_url,
|
|
||||||
headers=new_headers,
|
|
||||||
body=new_body,
|
|
||||||
syntax=request.syntax
|
|
||||||
)
|
|
||||||
|
|
||||||
# Check if any sensitive variables were actually used in the request
|
|
||||||
all_vars_in_request = set()
|
|
||||||
all_vars_in_request.update(VariableSubstitution.find_variables(request.url))
|
|
||||||
all_vars_in_request.update(VariableSubstitution.find_variables(request.body))
|
|
||||||
for key, value in request.headers.items():
|
|
||||||
all_vars_in_request.update(VariableSubstitution.find_variables(key))
|
|
||||||
all_vars_in_request.update(VariableSubstitution.find_variables(value))
|
|
||||||
|
|
||||||
# Determine if any sensitive variables were actually present
|
|
||||||
has_sensitive_vars = bool(
|
|
||||||
set(sensitive_variables) & all_vars_in_request
|
|
||||||
)
|
|
||||||
|
|
||||||
return redacted_request, all_undefined, has_sensitive_vars
|
|
||||||
|
|||||||
@ -62,19 +62,6 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
|
||||||
<!-- Redaction Indicator (shown when sensitive variables were redacted) -->
|
|
||||||
<child>
|
|
||||||
<object class="GtkImage" id="redaction_indicator">
|
|
||||||
<property name="icon-name">dialog-information-symbolic</property>
|
|
||||||
<property name="tooltip-text">Sensitive variables were redacted from this history entry</property>
|
|
||||||
<property name="valign">center</property>
|
|
||||||
<property name="visible">False</property>
|
|
||||||
<style>
|
|
||||||
<class name="warning"/>
|
|
||||||
</style>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
|
|
||||||
<!-- Delete Button -->
|
<!-- Delete Button -->
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="delete_button">
|
<object class="GtkButton" id="delete_button">
|
||||||
|
|||||||
@ -34,7 +34,6 @@ class HistoryItem(Gtk.Box):
|
|||||||
url_label = Gtk.Template.Child()
|
url_label = Gtk.Template.Child()
|
||||||
timestamp_label = Gtk.Template.Child()
|
timestamp_label = Gtk.Template.Child()
|
||||||
status_label = Gtk.Template.Child()
|
status_label = Gtk.Template.Child()
|
||||||
redaction_indicator = Gtk.Template.Child()
|
|
||||||
delete_button = Gtk.Template.Child()
|
delete_button = Gtk.Template.Child()
|
||||||
request_headers_label = Gtk.Template.Child()
|
request_headers_label = Gtk.Template.Child()
|
||||||
request_body_scroll = Gtk.Template.Child()
|
request_body_scroll = Gtk.Template.Child()
|
||||||
@ -78,10 +77,6 @@ class HistoryItem(Gtk.Box):
|
|||||||
|
|
||||||
self.timestamp_label.set_text(timestamp_str)
|
self.timestamp_label.set_text(timestamp_str)
|
||||||
|
|
||||||
# Show redaction indicator if sensitive variables were redacted
|
|
||||||
if self.entry.has_redacted_variables:
|
|
||||||
self.redaction_indicator.set_visible(True)
|
|
||||||
|
|
||||||
# Status
|
# Status
|
||||||
if self.entry.response:
|
if self.entry.response:
|
||||||
status_text = f"{self.entry.response.status_code} {self.entry.response.status_text}"
|
status_text = f"{self.entry.response.status_code} {self.entry.response.status_text}"
|
||||||
|
|||||||
@ -569,41 +569,12 @@ class RosterWindow(Adw.ApplicationWindow):
|
|||||||
if tab:
|
if tab:
|
||||||
tab.response = response
|
tab.response = response
|
||||||
|
|
||||||
# Create history entry with redacted sensitive variables
|
# Create history entry (save the substituted request with actual values)
|
||||||
# Determine which request to save to history
|
|
||||||
request_for_history = modified_request
|
|
||||||
has_redacted = False
|
|
||||||
|
|
||||||
if widget.selected_environment_id:
|
|
||||||
env = widget.get_selected_environment()
|
|
||||||
if env and widget.project_id:
|
|
||||||
# Get the project's sensitive variables list
|
|
||||||
projects = self.project_manager.load_projects()
|
|
||||||
sensitive_vars = []
|
|
||||||
for p in projects:
|
|
||||||
if p.id == widget.project_id:
|
|
||||||
sensitive_vars = p.sensitive_variables
|
|
||||||
break
|
|
||||||
|
|
||||||
# Create redacted request (only non-sensitive variables substituted)
|
|
||||||
if sensitive_vars:
|
|
||||||
from .variable_substitution import VariableSubstitution
|
|
||||||
request_for_history, _, has_redacted = VariableSubstitution.substitute_request_excluding_sensitive(
|
|
||||||
modified_request, env, sensitive_vars
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# No sensitive variables defined, use fully substituted request
|
|
||||||
request_for_history = substituted_request
|
|
||||||
elif env:
|
|
||||||
# Environment selected but no project - use fully substituted request
|
|
||||||
request_for_history = substituted_request
|
|
||||||
|
|
||||||
entry = HistoryEntry(
|
entry = HistoryEntry(
|
||||||
timestamp=datetime.now().isoformat(),
|
timestamp=datetime.now().isoformat(),
|
||||||
request=request_for_history,
|
request=substituted_request,
|
||||||
response=response,
|
response=response,
|
||||||
error=error,
|
error=error
|
||||||
has_redacted_variables=has_redacted
|
|
||||||
)
|
)
|
||||||
self.history_manager.add_entry(entry)
|
self.history_manager.add_entry(entry)
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user