Route finger search through the cacheable /finger/<term> path
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:
pmb
2026-06-17 10:24:16 -07:00
parent 3115ac64b2
commit c8786a2a0b
2 changed files with 37 additions and 29 deletions
+19 -24
View File
@@ -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)
success, output, is_error = run_finger_command(['finger'])
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())
return render_template('finger.html', title='Finger', result=result, error=error, username=username)
+14 -1
View File
@@ -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