This is an automated email from the ASF dual-hosted git repository.

gstein pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/steve.git

commit 1caa49b1ea136209a5b9a74cf6da3a3a2f64ff67
Author: Greg Stein <[email protected]>
AuthorDate: Fri Feb 20 05:49:34 2026 -0600

    refactor: update voting to use form-based submission with 
submitFormWithLoading
    
    Co-authored-by: aider (openrouter/x-ai/grok-code-fast-1) <[email protected]>
---
 v3/TODO.md                      | 12 +++++++++++-
 v3/server/pages.py              | 15 +++++++++++----
 v3/server/templates/vote-on.ezt | 29 ++++++++---------------------
 3 files changed, 30 insertions(+), 26 deletions(-)

diff --git a/v3/TODO.md b/v3/TODO.md
index ddbd3ee..dcc1129 100644
--- a/v3/TODO.md
+++ b/v3/TODO.md
@@ -39,4 +39,14 @@ Based on a review of `v3/server/pages.py` (and related 
templates like `voter.ezt
 - **Imports/Constants**: All seem correct and follow conventions.
 - **No Obvious Syntax/Logic Errors**: The code parses and runs logically, but 
the above are functional gaps.
 
-If you'd like to implement any of these fixes (e.g., add the date endpoints or 
fix upcoming elections), provide confirmation and details. Let me know if you 
have more context or want me to check specific sections!
+If you'd like to implement any of these fixes (e.g., add the date endpoints or 
fix upcoming elections), provide confirmation and details. Let me know if you 
have more context or want to check specific sections!
+
+## 8. CSRF Checking
+- **Issue**: CSRF tokens are placeholders and not validated in POST endpoints.
+- **Impact**: Vulnerable to CSRF attacks.
+- **Suggested Fix**: Implement a decorator to check CSRF tokens on POST 
endpoints (e.g., compare form/session token). Generate real tokens per session.
+
+## 9. Error Handling in `submitFormWithLoading`
+- **Issue**: If the server returns an error during form submission via 
`submitFormWithLoading`, the page doesn't reload, and the button stays 
disabled, potentially confusing users.
+- **Impact**: Poor UX on submission failures.
+- **Suggested Fix**: Investigate and add client-side error handling (e.g., 
re-enable button on failure, show error message).
diff --git a/v3/server/pages.py b/v3/server/pages.py
index 83a4e31..7b160bf 100644
--- a/v3/server/pages.py
+++ b/v3/server/pages.py
@@ -375,18 +375,25 @@ async def do_vote_endpoint(election):
 
     ### check authz
 
-    # Parse the JSON payload
-    data = await quart.request.get_json()
-    if not data:
+    # Parse the form data
+    form = edict(await quart.request.form)
+    if not form:
         await flash_danger('No vote data provided.')
         return quart.redirect(f'/vote-on/{election.eid}', code=303)
 
+    # Extract votes from form (keys like 'vote-<iid>')
+    votes = {}
+    for key, value in form.items():
+        if key.startswith('vote-'):
+            iid = key.split('-', 1)[1]
+            votes[iid] = value
+
     # Get the list of issues for this election
     issues = election.list_issues()
     issue_dict = {i.iid: i for i in issues}
 
     # Process each vote
-    for iid, votestring in data.items():
+    for iid, votestring in votes.items():
         if iid not in issue_dict:
             await flash_danger(f'Invalid issue ID: {iid}')
             return quart.redirect(f'/vote-on/{election.eid}', code=303)
diff --git a/v3/server/templates/vote-on.ezt b/v3/server/templates/vote-on.ezt
index f46cab0..9c1e4fd 100644
--- a/v3/server/templates/vote-on.ezt
+++ b/v3/server/templates/vote-on.ezt
@@ -21,6 +21,10 @@
       </button>
     </div>
 
+    <form id="voteForm" method="POST" action="/do-vote/[eid]">
+    <!-- CSRF token for form submission -->
+    <input type="hidden" name="csrf_token" value="[csrf_token]">
+
     <div id="issues-list" class="list-group">
     [for issues]
       <div class="list-group-item issue-item">
@@ -51,8 +55,9 @@
 
     <!-- Submit Button -->
     <div class="mt-4">
-      <button type="button" class="btn btn-primary" 
onclick="submitVotes()">Submit Votes</button>
+      <button type="button" class="btn btn-primary" id="submitVoteBtn" 
onclick="submitVotes()">Submit Votes</button>
     </div>
+    </form>
   </div>
 
     </div>
@@ -105,7 +110,7 @@
     // Submit votes
     function submitVotes() {
       const votes = {};
-      [# careful! be wary of open-brackets within ezt ]
+      // careful! be wary of open-brackets within ezt
       document.querySelectorAll('.vote-radio 
input[[]type="radio"]:checked').forEach(radio => {
         const issueId = radio.name.split('-')[[]1];
         votes[[]issueId] = radio.value;
@@ -116,24 +121,6 @@
         return;
       }
 
-      fetch('/do-vote/[eid]', {
-        method: 'POST',
-        headers: {
-          'Content-Type': 'application/json',
-        },
-        body: JSON.stringify(votes),
-      })
-        .then(response => {
-          if (!response.ok) throw new Error('Network response was not ok');
-          return response.json();
-        })
-        .then(data => {
-          alert('Votes submitted successfully!');
-          console.log('Server response:', data);
-        })
-        .catch(error => {
-          alert('Error submitting votes: ' + error.message);
-          console.error('Error:', error);
-        });
+      submitFormWithLoading('voteForm', 'submitVoteBtn', 'Submitting 
Votes...');
     }
   </script>

Reply via email to