Route finger search through the cacheable /finger/<term> path
Build and Push Docker Image / build-and-push (push) Failing after 2m19s
Build and Push Docker Image / build-and-push (push) Failing after 2m19s
The search form POSTed to /finger, an uncacheable URL with no search term in it, so every lookup re-ran the finger command even for repeated searches of the same user. Redirect any query/POST search to the canonical /finger/<username> path (and submit the form via GET, with a JS fast-path straight to that URL) so repeated lookups are served from the nginx response cache. A bare /finger with no user still lists local system users.
This commit is contained in:
@@ -146,36 +146,31 @@ def index():
|
||||
@app.route('/finger', methods=['GET', 'POST'])
|
||||
@limiter.limit(lambda: app.config['RATELIMIT_FINGER'])
|
||||
def finger():
|
||||
"""Finger command route"""
|
||||
"""Finger search form.
|
||||
|
||||
Any submitted username is redirected to the canonical
|
||||
``/finger/<username>`` path so the lookup happens on a cacheable URL with
|
||||
the search term in it — repeated searches for the same user are served from
|
||||
the nginx response cache instead of re-running the finger command. A bare
|
||||
``/finger`` with no user shows the local system users (no daemon enumeration
|
||||
risk: it never takes external input).
|
||||
"""
|
||||
username = (request.args.get('user', '')
|
||||
or request.args.get('username', '')
|
||||
or request.form.get('username', '')).strip()
|
||||
|
||||
if username:
|
||||
# Send the search to the cacheable canonical path.
|
||||
return redirect(url_for('finger_direct', username=username))
|
||||
|
||||
# No user supplied: list logged-in system users.
|
||||
result = None
|
||||
error = None
|
||||
username = request.args.get('user', '') or request.form.get('username', '')
|
||||
|
||||
if request.method == 'POST' or username:
|
||||
# Sanitize username input
|
||||
if username:
|
||||
# Allow alphanumeric characters, dots, hyphens, underscores, and @ symbol for email-style usernames
|
||||
if not all(c.isalnum() or c in '.-_@' for c in username):
|
||||
error = "Invalid username format. Only alphanumeric characters, dots, hyphens, underscores, and @ symbol are allowed."
|
||||
else:
|
||||
# Execute finger command with specific user
|
||||
cmd = ['finger', username]
|
||||
else:
|
||||
# Execute finger command without user (show all logged in users)
|
||||
cmd = ['finger']
|
||||
|
||||
if not error:
|
||||
# Execute the command with robust encoding handling
|
||||
success, output, is_error = run_finger_command(cmd)
|
||||
if success:
|
||||
result = output
|
||||
else:
|
||||
error = output
|
||||
app.logger.warning("finger lookup failed for %r from %s: %s",
|
||||
username, get_remote_address(), output.strip()[:200])
|
||||
elif username:
|
||||
app.logger.warning("rejected invalid finger username %r from %s",
|
||||
username, get_remote_address())
|
||||
success, output, is_error = run_finger_command(['finger'])
|
||||
if success:
|
||||
result = output
|
||||
else:
|
||||
error = output
|
||||
|
||||
return render_template('finger.html', title='Finger', result=result, error=error, username=username)
|
||||
|
||||
|
||||
+14
-1
@@ -5,7 +5,10 @@
|
||||
<div class="col-lg-10 mx-auto">
|
||||
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-3">
|
||||
<h1 class="h5 mb-0">🔍 Finger Command</h1>
|
||||
<form method="POST" action="{{ url_for('finger') }}" class="d-flex" role="search">
|
||||
<!-- GET so the search lands on a cacheable URL; JS sends it straight
|
||||
to /finger/<term>, and without JS the server redirects there. -->
|
||||
<form method="GET" action="{{ url_for('finger') }}" class="d-flex" role="search"
|
||||
onsubmit="return gotoFinger(event)">
|
||||
<input type="text" class="form-control form-control-sm me-2" id="username" name="username"
|
||||
value="{{ username or '' }}"
|
||||
placeholder="[email protected]"
|
||||
@@ -50,6 +53,16 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Navigate straight to the cacheable /finger/<term> path so the nginx response
|
||||
// cache is hit for repeated lookups (avoids a redirect round-trip).
|
||||
function gotoFinger(event) {
|
||||
var term = document.getElementById('username').value.trim();
|
||||
if (!term) { return true; } // empty -> let GET /finger list system users
|
||||
event.preventDefault();
|
||||
window.location.href = "{{ url_for('finger') }}/" + encodeURIComponent(term);
|
||||
return false;
|
||||
}
|
||||
|
||||
function clearForm() {
|
||||
document.getElementById('username').value = '';
|
||||
// Optionally reload the page to clear results
|
||||
|
||||
Reference in New Issue
Block a user