Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Function] Secure and Sanitize Cicero Api #978

Open
Alex-is-Gonzalez opened this issue Oct 17, 2024 · 1 comment
Open

[Function] Secure and Sanitize Cicero Api #978

Alex-is-Gonzalez opened this issue Oct 17, 2024 · 1 comment
Assignees
Labels
good first issue Good for newcomers

Comments

@Alex-is-Gonzalez
Copy link
Collaborator

Intro

In DevSecOps, "less privilege" refers to the principle of least privilege (PoLP), which means granting users, applications, or services the minimal level of access required to perform their tasks, and nothing more. When working with an API, this principle ensures that:

API users (such as applications or services) only have the minimum permissions they need to interact with the API. Each API key or token is assigned only the specific roles, access levels, or scopes necessary to perform a given function.Limiting exposure of sensitive data or operations by making sure an API consumer can only access certain endpoints or perform certain actions (e.g., read-only vs. read-write access).

Example in DevSecOps:

If you have an API that manages user data, and an application only needs to fetch user information, the API key associated with this app should only have read-only access to user data, not permissions to modify or delete it.
By enforcing least privilege, you minimize the risk of accidental or malicious damage in case the API key is compromised. Applying this principle helps to reduce security risks, ensuring that even if an account or service is compromised, the damage potential is limited.

Task

In file: https://github.com/OpenSourceFellows/amplify/blob/main/server/routes/api/representatives.js
Code snippet :

//lines 52 -142
// Get
router.get('/:searchText', async (req, res) => {
  const { searchText } = req.params
  const filter = req.query.filter
  const streetAddress = req.query.streetAddress

  /* check if valid ZIP code 
    ^\d{5} - the start of the line has 5 digits
    (-\d{4})?$ the end of the line has 4 digits preceded by a dash (optionally)
  */
  let isValidZIPcode = /^\d{5}(-\d{4})?$/.test(searchText)

  /* check if valid street address
     ^(\d{3,})\s? the start of the line can have 3 or more digits followed by a space
     (\w{0,})\s the next part can have 2 or more letters followed by a space
     ([a-zA-Z]{2,30})\s the next part must be an alphanetical string with 2-30 characters followed by a space
     ([a-zA-Z]{2,15}).?\s?  the next part must be an alphabetical string with 2-15 characters followed by a any digit (optionally) and/or space (optionally)
     (\w{0,5})$ the end of the line can have 0-5 letters
  */
  let isValidAddress =
    /^(\d{3,})\s?(\w{0,5})\s([a-zA-Z]{2,30})\s?([a-zA-Z]{2,15}).?\s?(\w{0,5})$/.test(
      streetAddress
    )

  if (!isValidZIPcode && !isValidAddress) {
    res.status(400).send({
      error:
        'Invalid zip code or street address format, valid examples of a ZIP code are 84054-6013 or 84054. The zipcode/street address used was ' +
        searchText
    })
    return
  }

  if (filter != null && !ALLOWED_JURISDICTION_FILTERS.includes(filter)) {
    res.status(400).send({
      error: 'Invalid jurisdiction filter. The filter used was ' + filter
    })
    return
  }

  try {
    const params = {
      order: 'district_type', // https://cicero.azavea.com/docs/#order-by-district-type
      sort: 'asc',
      max: 200,
      format: 'json',
      key: CICERO_API_KEY
    }
    // add ZIP code search as parameter.
    // NOTE: we check it first, because, as of now, the regex use to validate ZIP codes is stricter and more accurate
    if (isValidZIPcode) {
      params.search_postal = searchText
      params.search_country = 'US'
    }
    // add street address search as parameter
    else if (isValidAddress) {
      params.search_address = streetAddress
    }

    if (filter != null) {
      params.district_type = JURISDICTION_FILTER_MAP[filter]
    }

    const {
      data: { response }
      //cached
    } = await axios.get('https://cicero.azavea.com/v3.1/official', {
      params,
      paramsSerializer: (params) =>
        qs.stringify(params, { arrayFormat: 'repeat' }),

      cache: {
        // TODO: we are disabling the cache to test results first
        ttl: 1000 * 60 * 60 * 24 * 7 // the time until the cached value is expired in milliseconds: set to 1 week
      }
    })

    // if you want to check if response was cached, uncomment: this is a way to track the issue
    // console.log('isCached:', cached)

    const { errors, results } = response
    if (errors.length > 0) {
      throw new Error(errors.join(','))
    }
    if (
      !results ||
      !Array.isArray(results.candidates) ||
      results.candidates.length === 0
    ) {
      throw new Error('No matches found for the search criteria')
    }

Ask AI to do the following task by copying the code and the task list.

  1. Scoped Access to API Key:
    Only access the API key when absolutely necessary, not at the top of the file to apply the principle of least privilege
@Dunridge
Copy link
Collaborator

Branch: issue-978

@ramakanth98 ramakanth98 added the good first issue Good for newcomers label Dec 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue Good for newcomers
Projects
Status: No status
Development

No branches or pull requests

3 participants