Limit visit allows an administer to set a limit on how often a user can access a page or set of pages based on the path in an hour.

The use case this was developed for involves a directory of users which points back to the user's profile page. The user's profile page may contain more personal information and possibly contact information. Preventing the number of times an hour someone can visit a user profile page deters them from mining e-mail addresses.

Project page: https://drupal.org/sandbox/geekygnr/2242557
Git repository: git://git.drupal.org/sandbox/geekygnr/2242557.git

Review of other projects

I am putting down all the comments I made in the project review area. I don't count all of them as full reviews but I would like to show that I am looking at the applications and trying to help where I can.

Actual reviews

https://www.drupal.org/node/2299443#comment-9032559
https://www.drupal.org/node/2328483#comment-9098161
https://www.drupal.org/node/2328637#comment-9101379

Just trying to help out

https://www.drupal.org/node/2301609#comment-9031721
https://www.drupal.org/node/2238015#comment-9032355
https://www.drupal.org/node/2328637#comment-9098925
https://www.drupal.org/node/2328637#comment-9101129

CommentFileSizeAuthor
#19 pa_review_automate_issues.txt4.36 KBpushpinderchauhan
#11 z.png30.86 KBgisle

Comments

PA robot’s picture

Status: Needs review » Needs work

There are some errors reported by automated review tools, did you already check them? See http://pareview.sh/pareview/httpgitdrupalorgsandboxgeekygnr2242557git

We are currently quite busy with all the project applications and we prefer projects with a review bonus. Please help reviewing and put yourself on the high priority list, then we will take a look at your project right away :-)

Also, you should get your friends, colleagues or other community members involved to review this application. Let them go through the review checklist and post a comment that sets this issue to "needs work" (they found some problems with the project) or "reviewed & tested by the community" (they found no major flaws).

I'm a robot and this is an automated message from Project Applications Scraper.

geekygnr’s picture

Status: Needs work » Needs review

I have corrected all issues except one.

FILE: /var/www/drupal-7-pareview/pareview_temp/limitvisit.module
--------------------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
--------------------------------------------------------------------------------
7 | ERROR | Missing short description in doc comment
--------------------------------------------------------------------------------

I have been unable to find how to solve this problem and I believe this is a false positive because the code that generates this error is in a 8.x branch.

GoRoost’s picture

Status: Needs review » Needs work

Validated that comment #2 fix is accurate. Testing on stable d7 and saw the same error on Pareview.sh.

Aside from doc comment, Both Coder (Code Sniffer) and Pareview.sh come back clean.

- Module appears to work as expected, but only for logged in users. If public post, did not honor restrictions set by module. Based on use case provided above, this is likely not an important feature, but wanted to note it anyways.

- Need to t() value on line 42 - limitvisit.admin.inc
- Need to t() titles / descriptions in limitvisit.module

Aside from those small house-keeping fixes, I think you're good.

gbisht’s picture

Like Roost.Me I also found that module is taking all anonymous users as same because you are using uid to count user visits and uid for all anonymous users is 0. For this I think you should maintain visit count for anonymous user with some random value and store that random value in session or cookie so that they are separate for all anonymous users.

One more thing you module could be more useful if you can add functionality to assign role to particular rule, so that multiple rules can be add for same url with different roles.

Other than this your module looks good.

geekygnr’s picture

Status: Needs work » Needs review

I fixed the lines Roost.Me pointed out that needed t().

I am aware of the point Roost.Me and gulab.bisht made about anonymous users. Perhaps I should have added that as an issue on the project page or something. Like Roost.Me said it isn't a part of my use case so it isn't an issue for me. It is an improvement I would like to make and gulab.bisht your suggestion about a value in a cookie or session is a good one. Since dealing with anonymous users is out side my use case I have had a hard time finding the time to dedicate to it. Gulab.bisht, your suggestion about role specific rules is also good.

My hope was to get approval on the module as it functions now and then improve upon it later or accept community contributions. From what I read on the review process I wasn't expecting this to be a problem. If it is please let me know what functionality needs to be added for it to meet the requirements of the review process and I will see what time I can pull together to complete it.

Thank you for your input.

gisle’s picture

Status: Needs review » Needs work

Unfortunately, Roost.me gave you some bad advice about adding the t() function to "titles / descriptions".

Manual review

You should not be using t() in the hook_menu() title and description declarations. Menu automatically pushes title and description through t(), you'll end up with things being double-translated. See: function hook_menu in the API documentation.

Individual user account
OK. Applicant geekygnr is an individual user account.
Multiple applications
OK. No additional applications.
Licensing
OK. Follows the Drupal.org licensing requirements.
3rd party (non-GPL) code
OK. No 3rd party code.
Module duplication
OK. No duplication.
Project page
OK. Adequate presentation of project.
README.txt
OK. Follows guidelines.
Version specific branch
OK. (7.x-1.x)
Code length/complecity
OK. About 400 lines of code .
Security
OK. No security issues identified.
Coding style & Drupal API usage
OK except wrong use of t() in hook_menu().
geekygnr’s picture

Status: Needs work » Needs review

That may have been partially my fault, I was also missing them from the hook_permissions() so I got carried away with adding them.

I took out the t() from hook_menu().

gisle’s picture

Status: Needs review » Reviewed & tested by the community

Manual review

Individual user account
OK. Applicant geekygnr is an individual user account.
Multiple applications
OK. No additional applications.
Licensing
OK. Follows the Drupal.org licensing requirements.
3rd party (non-GPL) code
OK. No 3rd party code.
Module duplication
OK. No duplication.
Project page
OK. Adequate presentation of project.
README.txt
OK. Follows guidelines.
Version specific branch
OK. (7.x-1.x)
Code length/complecity
OK. About 400 lines of code .
Security
OK. No security issues identified.
Coding style & Drupal API usage
OK (one false positive in PAreview).
gisle’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +PAreview: security

Sorry, but I felt I had to do a more thorough review of this one, and the result of my second code walk-through is that it needs some work.

Automated Review

PAReview reported one issue (IMHO, this is a false positive).

Manual review

Individual user account
Yes: Applicant geekygnr follows the guidelines for individual user accounts.
No duplication
Yes
Master Branch
Yes: Follows the guidelines for master branch.
Licensing
Yes: Follows the licensing requirements
3rd party code/content
Yes: Follows the guidelines for 3rd party code.
README.txt
Yes: Follows the guidelines for in-project documentation.
Code long/complex enough for review
Yes, about 400 lines. Follows the guidelines for project length and complexity.
Secure code
No: Does not follow the guidelines for writing secure code.
In function limitvisit_admin_redirect, the field value is not sanitized. Entering <script>alert('XSS');</script> triggers an immediate XSS.
Coding style & Drupal API usage
My notes:
  • Typo in docblock for hook_permission() ("Implements hook_permissions().")
  • Even though many modules don't use the second parameter for variable_get(), it is best practice to use it. If you don't provide a default value, there will be an error if the variable requested does not exist.
  • (*) Do not call time(). Use REQUEST_TIME instead.
  • (*) Use placeholders for variables in a translated string.

    Not: $form_state['values']['value'] . ' ' . t('is not a valid path.'), but:

    t('@value is not a valid path.', array('@value' => $form_state['values']['value'])).
  • (*) Data from limitvisit_admin_form is not validated before they're inserted into the database. Putting "X" in the field for hours triggers an PDOexception.
  • (*) Not all user data is sanitized (re: security review above).

The starred items (*) are fairly big issues and warrant going back to Needs Work. The rest of the comments in the code walkthrough are recommendations.

Please don't remove the PAReview: security security tag, we keep that for statistics and to show examples of security problems.

geekygnr’s picture

gisle can you walk me through how you got the XSS. I put that string in the field and submitted but it gave me the error that it wasn't a valid path and an alert didn't pop up.

I even tried making that the #default_value to see if that is what you meant and I didn't get an XSS.

The rest of the stuff... ya I really should have known better. Thanks for spotting it for me I will clean that up.

gisle’s picture

StatusFileSize
new30.86 KB

It comes from the "is not a valid path" error in the message area (note that the part of the message that should have echoed the path is missing).

To reproduce, put the CSS exploit in the textfield and press "Save Redirect" (as shown below):

Link to screenshot.

Using the correct placeholder for variables in a translated string fixes it. Do not have:

$form_state['values']['value'] . ' ' . t('is not a valid path.')

But instead:

t('@value is not a valid path.',
  array('@value' => $form_state['values']['value']))
geekygnr’s picture

Status: Needs work » Needs review

Well it is a little scary that I haven't been able to reproduce this but I made the change.

The latest change should cover everything on your list gisle. I need to look up and see if there is a way to have form_set_error highlight a field that is part of a array but that is a small UI issue.

geekygnr’s picture

I found the solution to my form_set_error problem. I was also able to reproduce the XSS on a friends mac.

The problem seems to be on my work station.

gisle’s picture

Status: Needs review » Needs work

Your code is still not secure:

Insecure (your code):

form_set_error('value', t('!path is not a valid path.',
  array('!path' => $form_state['values']['value'])));

Secure:

form_set_error('value', t('@path is not a valid path.',
  array('@path' => $form_state['values']['value'])));

Please read Altered the behaviour of placeholders in t() calls and notice that placeholders starting with "!" are not sanitized, but inserted as-is. Only placeholders starting with "@" and "%" are sanitized.

The rest is OK.

geekygnr’s picture

Status: Needs work » Needs review

That's weird I went off the e-mail notification I got about your comment and it !value as the example. My bad I have looked at the difference between in them in the past I just wasn't thinking.

I made the change.

deviantintegral’s picture

I don't think anything below blocks this from being promoted - it's mostly "would be nice" suggestions. They could probably be forked into new issues in the project queue. Everything else looks totally fine to me, so +1 on promoting this.

In README.txt:

To avoid someone from signing into the directory and scraping it for everyone's personal information.

This isn't a complete sentence - how about "For example, this can be used to help limit simple scraping of profile information from users with public profiles."

drupal

It's "Drupal" throughout the file :)

The paht you enter can

Typo here.

In limitvisit.admin.inc:

'#value' => t('Save Redirect'),

Drupal uses sentence case for buttons - so "Save redirect". There's probably a few other places for this too.

In limitvisit_admin_redirect_validate() - you could simplify that a little by using an #element_validate callback on the text field. Then you can use form_error() directly. What you have is still perfectly functional though.

In limitvisit.module:

In limitvisit_boot() - you could probably use the caching API to save on a DB call to load rules, especially as you can easily clear / reset it when you save the admin form.

You might be able to simplify some of this by using the Flood API - https://api.drupal.org/api/drupal/includes!common.inc/function/flood_is_...

geekygnr’s picture

I fixed the deviantintegral's points on README.txt and limitvisit.admin.inc

I am going to have to do some reading about the cache API and Flood API before I get to those changes.

geekygnr’s picture

I applied the cache API as per deviantintegral's suggestion and fixed some bugs I came across.

pushpinderchauhan’s picture

Status: Needs review » Needs work
StatusFileSize
new4.36 KB

@geekygnr, thankyou for your contribution.

Automated Review

PAReview reported some issues. See attachment.

Manual Review

Individual user account
Yes: Follows the guidelines for individual user accounts.
No multiple applications
Yes: No multiple application found.
No duplication
Yes: Not found any similar project.
Master Branch
Yes: Follows the guidelines for master branch.
3rd party code
Yes: Follows the guidelines for 3rd party code.
README.txt
Yes.
Code long/complex enough for review
Yes. Follows the guidelines for project length and complexity.
Secure code
Yes.
Coding style & Drupal API usage
1. You are opening up your README.txt file in hook_help() that's incorrect. I would personally recommend you, just give specific information about your module inside hook_help().
2. In following if statement, this code && !empty($element['#value']) can be removed because drupal_valid_path() take care of this. Also you can use #required => TRUE property with your form element.

if (!drupal_valid_path($element['#value']) && !drupal_lookup_path('source', $element['#value']) && !empty($element['#value'])) {

3. IMO, both field should be mandatory in limitvisit_admin_form() form. Also Allowed visits per hour field contains validation for numeric value, it should be required. Because If it accept blank value then it contradict with your validation.

As above point looks blocker to me so I am moving this to "NEED WORK".

As I am not a git administrator, so I would recommend you, please help to review other project applications to get a review bonus. This will put you on the high priority list, then git administrators will take a look at your project right away :-)

Thanks Again!

gisle’s picture

er.pushpinderrana wrote:

You are opening up your README.txt file in hook_help() that's incorrect.

@er.pushpinderrana, why are you saying that? Can you refer to a documentation guideline stating that showing the README.txt file in hook_help() is incorrect?

Here is a link to the text about hook_help() in the official Module documentation guidelines. There is even an example showing how to display README.txt or README.md in hook_help().

pushpinderchauhan’s picture

@gisle, thank you for correcting me.

geekygnr’s picture

Status: Needs work » Needs review

In following if statement, this code && !empty($element['#value']) can be removed because drupal_valid_path() take care of this.

@er.pushpinderrana the !empty condition is there to accept an empty field. When that field is empty there is a defined behavior.

I have fixed the errors pareview.sh found and added some required field flags to the form. I couldn't add them to the fields used to create a new rule since that would force a user to create a new rule when all they want to do is edit an existing rule.

geekygnr’s picture

Issue summary: View changes
geekygnr’s picture

Issue summary: View changes
Michael Hodge Jr’s picture

Status: Needs review » Reviewed & tested by the community

Automated Review

There is one error, but from comment #2 and #3, it's a false positive so you are good to go on the automated review front.

Manual Review

In your .install file, you define schema for storing data, but don't get rid of that schema when uninstalling the module. I would suggest adding drupal_uninstall_schema('limitvisit'); to hook_uninstall

Other than that I didn't see any issues as I went through and reviewed your code. I'm going to set this to RTBC as I don't feel the lack of the uninstall_schema warrants holding up the review process.

geekygnr’s picture

@Michael Hodge Jr, from what I have read the drupal_uninstall_schema is a practice from D6 that is no longer needed in D7 (https://www.drupal.org/update/modules/6/7#install-schema). I did a complete uninstall of my module and the database tables were removed.

Michael Hodge Jr’s picture

Ah you are correct! I wasn't aware that it wasn't needed anymore. Thanks for pointing that out!

geekygnr’s picture

Issue summary: View changes
geekygnr’s picture

Issue summary: View changes
geekygnr’s picture

Issue summary: View changes
geekygnr’s picture

Issue summary: View changes
Issue tags: -PAreview: security +PAReview: security PAReview: review bonus
geekygnr’s picture

Issue tags: -PAReview: security PAReview: review bonus +PAreview: security, +PAreview: review bonus
klausi’s picture

Status: Reviewed & tested by the community » Fixed

manual review:

  1. limitvisit_boot(): why do you need hook_boot() and cannot use hook_init()? Since your functionality only works for authenticated users this should work with hook_init() as well? Counting the visits for anonymous users is pretty pointless since all of them will be counted under user 0? Please add a comment.
  2. limitvisit_boot(): maybe the 3600 seconds interval should be configurable with a variable?
  3. Why are you using the HTTP status code 307? Please add a comment.
  4. Since you can basically shut down the entire drupal site as admin if you specify a very broad path I would recommend to mark the admin permission as 'restrict access' => TRUE, so that the users know that people can do damage with this permission.

But that are not critical application blockers, so ...

Thanks for your contribution, geekygnr!

I updated your account so you can promote this to a full project and also create new projects as either a sandbox or a "full" project.

Here are some recommended readings to help with excellent maintainership:

You can find lots more contributors chatting on IRC in #drupal-contribute. So, come hang out and stay involved!

Thanks, also, for your patience with the review process. Anyone is welcome to participate in the review process. Please consider reviewing other projects that are pending review. I encourage you to learn more about that process and join the group of reviewers.

Thanks to the dedicated reviewer(s) as well.

geekygnr’s picture

Thank you for the approval klausi. In response to your questions.

limitvisit_boot(): why do you need hook_boot() and cannot use hook_init()? Since your functionality only works for authenticated users this should work with hook_init() as well? Counting the visits for anonymous users is pretty pointless since all of them will be counted under user 0? Please add a comment.

It's been a while but I remember when first developing this modules I tried for hook_init and hook_boot. At the time everything worked in hook_boot but not in hook_init. I couldn't really figure out the reason for it and I haven't gone that in depth into the bootstrap process.

limitvisit_boot(): maybe the 3600 seconds interval should be configurable with a variable?

Right now I am forcing them to pick number of visits per hour but I will add this as a feature request to my project for future development.

Why are you using the HTTP status code 307? Please add a comment.

I used the 307 Temporary Redirect because it is a temporary redirect since that redirect will only exist for an hour. It seemed the most appropriate to me when I first started. Re-reading the Status Code Definations I see the reason for your question and after further reading I found that rfc2616 is obsolete. I found rfc7231 and from those descriptions I will change the status code to 403. It will also keep it consistent with the access_denied default.

Since you can basically shut down the entire drupal site as admin if you specify a very broad path I would recommend to mark the admin permission as 'restrict access' => TRUE, so that the users know that people can do damage with this permission.

Thank you for that point I will mark the admin permission as you suggested.

Thanks to everyone for their reviews and help.

geekygnr’s picture

An update on the http status 307. drupal_goto doesn't work with 4xx status code errors and I don't know of a way to get around this.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.