Mass Assignment Php To Usd

Assignment 1:

First submission due on CMS by Monday, February 18th at 11:59 pm.
These instructions last updated on February 24 2013 19:55.
Any changes from the handout marked in orange.
Feb 14 04:42am: Clarification on iscurrency spec (see Piazza @42 followup)
Feb 17 10:49pm: Removed stray cut-and-paste error from specification of exchange (see Piazza @68)
Feb 18 10:26pm: Explained that after partners group on CMS, only one partner submits the code.
Feb 19 11:04pm: Explained in Section 10.1 where your feedback can be found.
Feb 24 2:46pm: Explained in Section 10.1 how to contact your grader.
Feb 24 2:46pm: Added more info about picking test cases in Section 6.

Authors: W. White, Q. Jia, L. Lee, S. Marschner; image credit: AFP (originating page)


Thinking about that trip overseas? Best to go when the exchange rate is in your favor, if you can swing it: when your dollars buy more in the foreign currency, you can do more on your vacation. Hence, it would be nice to have a function that, given your current amount of cash in US dollars, tells you how much your money is worth in another currency.

However, there isn't a set mathematical formula to compute this conversion; rather, the value of one currency with respect to another is constantly changing. In fact, in the time that it takes you to read this paragraph, the exchange rate between the dollar and the Euro has probably changed several times. How on Earth do we write a program to handle something like that?

Our answer is to make use of a web service. A web service is an program that, when you send it web requests, automatically generates a web page with the information that you asked for. In our case, we'll employ a web service that tells us the current exchange rate for most major international currencies. Your job will be to use (surprise!) string methods to get and read the web page and extract the exact information we need.

Learning Objectives

This assignment is designed to give you (more) practice with the following skills:

  • How to write a self-contained module in Python
  • How to use string and object methods in Python
  • How to connect Python to a web service
  • How to read specifications and understand preconditions
  • How to use docstrings appropriately for specifications
  • How to follow the coding conventions for this course
  • How to thoroughly test a program

The functions you will write should be relatively short and straightforward. The emphasis in A1 is testing and “good practices”, as introduced in lecture and lab, rather than complicated computation.

Start Early: 300 students trying to contact a web service at once could slow everybody down

Connecting to, and reading from, a web page is not instantaneous. It will take several seconds for some of the functions you will write to complete. Furthermore, if you wait until the last minute to test this assignment, you will be connecting to the same web page as everyone else in the class, so things could slow down even more.

Academic Integrity and Previous Versions of This Assignment

This assignment is a slightly modified version of an assignment given the previous semester. Please do this assignment without consulting (or seeking) previous solutions. First, you are allowed to revise and resubmit, with help from us, until you have mastered the skills you'll need in subsequent assignments, so there's no reason to not do this assignment on your own (or with your partner). Second, to consult any prior solutions is a violation of CS1110's academic integrity policies.

Table of Contents

  1. Before You Get Started
  2. The Currency Exchange Web Service
  3. How To Proceed Through A1: Iterative Development
  4. Part A: Breaking up Strings
  5. Part B: Processing a JSON String
  6. Part C: Currency Query
  7. Part D: Currency Exchange
  8. Finishing the Assignment
  9. Appendix: Connecting to Google

Before You Get Started

Read Carefully

These instructions may seem long, but that's because we have aimed to give you all the information you need in one document. Your chances of completing the assignment quickly and correctly will be increased by reading carefully and following all instructions. Many requests for resubmission and regrades are caused not by issues with programming but simply by not following directions.

Pay particular attention to the section on Iterative Development, because it contains important instructions for the remaining sections, and we will not repeat these instructions for each section.

Grading Policy (Revise-and-Resubmit Cycle)

To ensure that everyone masters the material in this assignment, we use an iterative feedback process. If your code doesn't meet one or more of the three criteria below, we will give you feedback, and assign you a new “grade” indicating the number of (re-)submissions so far, so we can keep track of your progress. You should then revise and resubmit your code. This process will be repeated until your code meets all the criteria, at which point we will give you full marks (a score of 10). The achievement of mastery should be recorded by Monday, February 25th. In order for this to happen, you should check CMS for a grade update, and revise if you're asked to, at least once a day from the deadline for your first submission, Monday, February 18th. In our experience, almost everyone is able to achieve a perfect score within two revisions. Again, do not be alarmed if you see a grade of "1" for the assignment at first, because that's just our notation for saying that you submitted your first try!

Our evaluation of your code will focus on the following criteria, in order:

  • appropriateness of function specifications and formatting with respect to the course style guidelines, available on the course web page.
  • adequacy of the test cases
  • correctness of the code

Collaboration Policy

You may do this assignment with one other person. If you are going to work together, then form your group on CMS by Monday, February 18th. Both people must do something to form the group: The first person proposes, and then the other accepts. You have to do this early because CMS doesn't allow you to form groups once grades are released. Once you've grouped on CMS, only one person submits the files.

If you do this assignment with another person, you must work together. It is against the rules for one person to do some programming on this assignment without the other person sitting nearby and helping. Take turns "driving"; alternate using the keyboard and mouse.

With the exception of your CMS-registered partner, you may not look at anyone else's code or show your code to anyone else (except a CS1110 staff member) in any form whatsoever.

Warning: If you and your partner use different OS's (e.g., one uses Windows, the other Mac), please be aware that these platforms handle whitespace differently. You may find that when you trade files back and forth, spaces become tabs and vice versa. Follow the guidelines for making whitespace visible in Komodo Edit.

Assignment Scope: no if-statements, loops or recursion

Everything that you need to complete this assignment has been covered by the 6th lecture and 3rd lab. In particular, you may not use if-statements anywhere in this assignment, because they are actually superfluous and heavy-handed for the task. Similarly, students with prior programming experience should not use loops or recursion.

Getting Help

If you do not know where to start, find your progress has stalled, or become lost along the way, please see someone immediately; a little in-person help can do wonders. See the staff page for who you can see when. Piazza is also an excellent resource for getting questions answered quickly (although you are not allowed to post your code there).

The Currency-Exchange Web Service

Before you start, it's useful to play around a little with the currency-exchange web service, just using your web browser.

For this assignment, you will use a simulated currency exchange service that never changes exchange rates. This is important for testing; if the answer is always changing, it is hard to test that you are getting the right answers. (The appendix explains how you can make a few minor changes to hook your program up to Google's currency calculator to get real-time currency-exchange results, if you like, but don't submit such code for your assignment.)

To use the service, you employ special URLs that start with the following prefix: This prefix is followed with a currency query; a currency query has three pieces of information in the following format (without spaces; we've included spaces here solely for readability): q=amountsource =? target where amount is a float, source is a three-letter code for the original currency, and target is a three-letter code for the new currency. For example, if you want to know the value of 2.5 dollars (USD) in Euros (EUR), the query is q=2.5USD=?EUR The full URL for this query is Click on the link to see it in action.

You will note that the “web page” that shows up in your browser is just a single line in the following format:

{money_in: "2.5 U.S. dollars",money_out: "2.0366598775 Euros",error: "",icc: true} This is what is known as a JSON representation of the answer; JSON is a way of encoding complex data. You will use your string-processing know-how to pull out the relevant data from JSON strings.

Try a few more currency queries to familiarize yourself with the service. Note that if you enter an invalid query (for example, using a non-existent currency code like "AAA"), you will get the following response, indicating an error:

{money_in: "",money_out: "",error: "4",icc: false} This will be important for error handling in this assignment.

Focus of the Assignment

Your primary goal in this assignment is to use the currency-exchange service to write the following function:

def exchange(amount_from, currency_from, currency_to): """Returns: amount of currency received in the given exchange. In this exchange, the user is changing <amount_from> money in currency <currency_from> to the currency <currency_to>. The value returned is a float representing the amount in currency <currencyTo>. Preconditions: <amount_from> is a float. Both <currency_from> and <currency_to> are strings that are valid three-letter currency codes."""

This function will involve several steps: getting the JSON string from the web service, breaking up the string to pull out the numeric value, and converting that value to a float. As this is the very first assignment, we are going to take you through this process step-by-step.

You will notice that the assignment steps might seem ordered “in reverse”: we have you write the functions to break up the string first, and the functions to interact with the web service last. One reason we've chosen this ordering is to focus on the string-processing aspects of the task.

Currency Exchange Table

In order to make it easier to test your program, we have fixed the exchange rates in our web service. That way you can test the answer in a web browser (using a currency query URL) and then compare the results to your Python program, without worrying about rates fluctuating.

The following currencies are supported by our web service:

CodeName1 USD = CodeName1 USD =
AEDUnited Arab Emirates dirham3.67300014MADMoroccan dirham8.96073406
ANGNetherlands Antilles guilder1.74999869MDLMoldovan leu12.4599723
ARSArgentine peso4.60600254MKDMacedonian denar50
AUDAustralian dollar0.952743902MURMauritian rupee30.7002732
BGNBulgarian lev1.59330049MXNMexican peso13.1385327
BHDBahrain dinar0.377009934MYRMalaysian ringgit3.12949865
BNDBrunei dollar1.25080051NADNamibian dollar8.26268736
BOBBolivian boliviano7.01001731NGNNigerian naira157.05984
BRLBrazil real2.02269873NIONicaraguan cordoba23.689946
BWPBotswana pula7.73395205NOKNorwegian krone5.96630232
CADCanadian dollar0.988601426NPRNepalese rupee89.3415528
CHFSwiss franc0.978396994NZDNew Zealand dollar1.23823675
CLPChilean peso482.392668OMROmani rial0.384949995
CNYChinese yuan6.36589915PENPeruvian nuevo sol2.61500122
COPColombian peso1821.49362PGKPapua New Guinean kina2.12404418
CRCCosta Rican colon499.5005PHPPhilippine peso42.2904508
CZKCzech koruna20.2889141PKRPakistan rupee94.3930527
DKKDanish krone6.06369303PLNPolish zloty3.3252972
DOPDominican peso39.1251614PYGParaguayan guarani4424.77876
DZDAlgerian dinar82.1962847QARQatar riyal3.64090484
EEKEstonian kroon11.7303429RONRomanian lei3.65570564
EGPEgyptian pound6.07651548RSDSerbian dinar96.2927299
EUREuro0.814663951RUBRussian ruble31.8969092
FJDFiji dollar1.78253119SARSaudi riyal3.75009375
GBPBritish pound0.636861546SCRSeychelles rupee13.000013
HKDHong Kong dollar7.75662804SEKSwedish krona6.70627842
HNLHonduran lempir19.5000195SGDSingapore dollar1.25080051
HRKCroatian kuna6.06818209SKKSlovak koruna24.5428887
HUFHungarian forint226.8088SLLSierra Leonean leone4329.00433
IDRIndonesian rupiah9523.80952SVCSalvadoran colon8.74997813
ILSIsraeli shekel4.04799301THBThai baht31.5497224
INRIndian rupee55.8784086TNDTunisian dinar1.62299986
JMDJamaican dollar89.1821992TRYTurkish lira1.79870026
JODJordanian dinar0.708501307TTDTrinidad dollar6.4
JPYJapanese yen79.1577614TWDTaiwan dollar29.987705
KESKenyan shilling84.0689365TZSTanzanian shilling1579.77883
KRWSouth Korean won1132.50283UAHUkrainian grivna8.0990022
KWDKuwaiti dinar0.282719878UGXUgandan shilling2481.38958
KYDCayman Islands dollar0.820001476USDU.S. dollar1
KZTKazakh tenge149.276011UYUUruguayan peso21.1501449
LBPLebanese pound1506.0241UZSUzbekistani sum1912.04589
LKRSri Lankan rupee132.013201VNDVietnamese dong20833.3333
LTLLithuanian litas2.8127971YERYemeni rial215.517241
LVLLatvian lats0.567099174ZARSouth African rand8.26268736
MADMoroccan dirham8.96073406ZMKZambia kwacha4878.04878

Note, however, that you should not use the values in this table in any of the functions that you write in . The table above is for testing your functions; not for writing them, and there is no reason for you to waste your time hard-coding in each of the approximately zillion currencies listed in this table into your program, since the web service you'll contact already knows them all anyway.

How To Proceed Through A1: Iterative Development

One of the most important outcomes of this assignment is that you understand the importance of testing. This assignment will follow an iterative development cycle. That means you will write a few functions, then fully test them before you write any more. This process makes it easier to find bugs; you know that any bugs must have been part of the work you did since the last test.

In this section we help you get started with this process. We also provide an overview of the rest of the assignment.

Setting up

To do this assignment, Python and the Cornell Extensions must be set up properly on the computer you plan to work on. If you have not already done this, follow the installation instructions to set it up on your computer. Alternatively, you can just work in the ACCEL lab.

It is possible to do this assignment without the command shell, provided that you have added a "run button" to Komodo Edit. However, we suggest that you run your unit test on the command line (type ) when you get near the end of the assignment, for reasons given below.

Finally, create a folder on your hard drive that is dedicated to this assignment and this assignment only. Every time that you work on a new assignment, we want you to make a new folder, to keep things organized and avoid problems with naming collisions. Make sure that the command shell and Komodo Edit are both open in the current folder before you start.

Put the file in this directory.

The Module

In your newly created directory, you should create the module (with file name ). This will be the main module for this assignment. Following the style guidelines, the first three lines of this file should be single-line comments with (1) the module name, (2) your name and netid, and (3) the date the file was last edited. Immediately after this, add the following docstring:

"""Module for currency exchange This module provides several string-parsing functions to implement a simple currency-exchange routine using an online currency service. The primary function in this module is exchange()."""

This docstring is the module specification. We recommend that you directly cut-and-paste this docstring into . For now, we want to expose you to specifications, not have you write them on your own.

The Module

Iterative development hinges on proper unit testing, which was covered in lecture and lab. In the same folder as , create the module (with file name ). This will be the unit test for the module.

As with , the first three lines of this file should be the authoring-info 3-comment header. Immediately after it, add the following Python code:

"""Unit test for module a1 When run as an application, this module invokes several procedures that test the various functions in the module a1."""import cunittest import a1

Add four procedure stubs to this : , , , , separated from each other by two blank lines. Remember that a procedure stub has a function header and then the keyword (indented) after the header, but nothing else. You'll add test cases to these procedures later.

Finally, at the end of , add the following application code (see lecture slides for more on application code).

if __name__ == "__main__": testA() testB() testC() testD() print "Module a1 passed all test cases"

The application code will call your four test procedures, which are (currently) empty. If everything is working, then the module, when run, prints out the message

"Module a1 passed all test cases"
Try it.

Instructions for the Remainder of the Assignment

The rest of the assignment is broken into Parts A, B, C, and D. In each part, do the following:

  1. For each function we ask you to write, write its header in Then, admire the completeness of the specifications we give you for each function, and then copy each one (copy-and-paste is fine) into the corresponding function body, indented.
  2. Add test cases to the appropriate test procedure in for each function — yes, before writing the function bodies, as we talked about in lecture.
  3. Write the function bodies so that the functions satisfy their specifications.
  4. Run the unit test . If errors are found, fix them and re-test; rinse and repeat until no more errors are found.

Unless otherwise instructed, each test case should be a call one of the assertion procedures. Your tests should be representative, meaning that they should cover the space of possible inputs; we exercised this concept in lab.

If You Choose to Craft Your Own Specifications

The descriptions that we provide in each part below represent the level of completeness and precision we are looking for in your docstring comments. In fact, it is best to copy-and-paste these descriptions to create the first draft of your docstring comments. If you do not copy-and-paste, please adhere to the conventions we use, such as using a single line, followed by a blank and a more descriptive paragraph, using "Returns: ..." for fruitful functions, and so on. Using a consistent set of good conventions in this class will help us all.

One way to check that your specifications are written correctly is to start an interactive Python shell and type

>>> import a1 >>> help(a1)
This should list all the functions with their specifications.

Part A: Breaking Up Strings

Back to extracting currency information from a JSON string. The first step we want you to take is Conceptually, our first goal is to be able to separate the currency amount from the currency name. For example, if we are given the string

"0.814663951 Euros" then we want to break it up into "0.814663951" and "Euros".

We guarantee that there will be no spaces in the currency amount, and that the first space will be before the currency name (though there may be later spaces in the currency name itself, such as "U.S. Dollar"). Hence we just need the following two functions. To accomplish this task, you are to write two general helper functions, which can apply to more than just strings representing currency amounts, but to any strings containing at least one space.


Returns: Substring of <s> up to, but not including, the first space

Precondition: <s> has at least one space in it


Returns: Substring of <s> after the first space

Precondition: <s> has at least one space in it

Implement these functions according to their specifications in the way described in the section “Instructions for the Remainder of the Assignment” above: write header and specification in ; give in a specification about what functions it's testing and write test cases for your functions in ; fill in the function body in ; test by running ; correct any errors and re-test until no errors remain. Your function bodies will probably have only one or two lines and use find() or index(). Your test cases should make use of in . Our implementation has four test cases for each of the two functions above. When you think about what test cases you want to include, consider: does the specification allow for strings with more than one space? Strings that start with a space? Strings that don't have any spaces?

Part B: Processing a JSON String

All responses to a currency query to our web service, whether valid or invalid, contain the keywords "money_in" and "money_out". In the case of a valid currency query, the answer is in quotes after the keyword "money_out". If it is invalid, then the quotes after money_out are empty. Hence the next step is to extract the information in quotes after these keywords.

First, add the following function to Again, do this by following the steps outlined in “Instructions for the Remainder of the Assignment”: put test cases in before writing function bodies in .

Make sure you've appropriately specified testB().


Returns: The first substring of s between two double-quote characters

A quote character is one that is inside a string, not one that delimits it. We can use ' to delimit the string if we want to use " inside it.

Example: If s is , this function returns
Example: If s is , this function still returns because it only picks the first such substring.

Precondition: <s> is a string with at least two double-quote characters inside.


Next, add the following functions. To create test cases, you will need to try out several JSON responses. To get some JSON responses for testing, enter a query URL into the web service and copy the result into a test case.


Returns: The money_in value in the response to a currency query.

Given a JSON response to a currency query, this returns the string inside quotes (") immediately following the keyword money_in. For example, if the JSON string is

'{money_in: "2 U.S. dollars",money_out: "1.629327902 Euros",error: "",icc: true}' then this function returns (not ). It returns the empty string if the JSON string is the result of an invalid query.

Precondition: <query> is the response to a currency query


Returns: The money_out value in the response to a currency query.

Given a JSON response to a currency query, this returns the string inside quotes (") immediately following the keyword money_out. For example, if the JSON string is

'{money_in: "2 U.S. dollars",money_out: "1.629327902 Euros",error: "",icc: true}' then this function returns (not ). It returns the empty string if the JSON string is the result of an invalid query.

Precondition: <query> is the response to a currency query


Returns: The error value in the response to a currency query.

Given a JSON response to a currency query, this returns the string inside quotes (") immediately following the keyword error. For example, if the JSON string is

'{money_in: "",money_out: "",error: "4",icc: false}' then this function returns (not or the number ). The returned string will be non-empty if a currency-conversion error occurred, for example, if invalid currency keywords were supplied in the query. It returns the empty string if the JSON string is the result of a valid query.

Precondition: <query> is the response to a currency query

You should not need a conditional statement to implement these functions; simply find the position of the appropriate keyword and extract the value in quotes immediately after it. Your implementations must use the find or index() string methods, plus the helper function you already wrote (or a function that uses ).


Part C: Currency Query

Now it is time to interact with the web service. In this part, you will implement a single function. The test cases should go in procedure in ; don't forget to specify properly.

currency_response(amount_from, currency_from, currency_to)

Returns: A JSON string that is a response to a currency query.

A currency query converts <amount_from> money in currency <currency_from> to the currency <currency_to>. The response should be a string of the form

'{money_in: "<old-amount>",money_out: "<new-amount>",error: "",icc: true}' where the values <old-amount> and <new-amount> contain the value and name for the original and new currencies. If the query is invalid, both <old-amount> and <new-amount> will be empty.

Preconditions: <amount_from> is of type float, while <currency_from> and <currency_to> are of type string.

While this function sounds complicated, it is actually the simplest function so far and can be implemented in two lines. You need to use the function from the module that we saw in lab 2. Recall that this function takes a string that represents a URL and returns an object of type “instance” that represents the web page for that url. Such an object has the following methods:

geturl()Returns: The URL address of this web page as a string.
read()Returns: The contents of this web page as a string.

Using one or both of these methods is enough to implement the function above.


You need to ensure that the function returns exactly the right JSON string for the value given. The best way to test this is to enter a web-service URL, such as , in your browser to manually get the right JSON answer and copy it into a test case in ; then check that the function returns the same JSON string. Remember to be thorough with your choice of test cases; one is not enough.


Fetching a web page takes time, especially if too many people are trying to do so at the simultaneously. You should give each call to this function at least 10-20 seconds to complete before restarting any tests.

There is another issue if you are running the unit test with the "run button" and not from the command shell. It appears that Komodo delays all of the print statements until the unit test is complete. So you will see nothing for a while (almost a minute, if you have a lot of tests) and then you will see everything at once. This can get confusing if there are multiple outputs, for instance, if you have put in many print statements to debug your code. We recommend running this unit test from the command shell, so that you can see each print statement the second it is executed: type at the command prompt (not the Python command prompt).

Part D: Currency Exchange

We are now ready for the final part of the assignment. Implement the following functions according to their specification, again using our test-case-before-function-body methodology. The test cases should go in procedure in , which you should properly specify. You may wish to use as opposed to in some of your test cases.


Returns: True if <currency> is a valid 3-letter code for a currency, False otherwise

Precondition: <currency> is a string.

exchange(amount_from, currency_from, currency_to)

Returns: amount of currency received in the given exchange.

In this exchange, the user is changing <amount_from> money in currency <currency_from> to the currency <currency_to>. The value returned is a float representing the amount in currency <currencyTo>. Preconditions: <amount_from> is a float. Both <currency_from> and <currency_to> are strings that are valid three-letter currency codes.

In implementing , you should not use the table of currencies. That would make a very large function with a lot of if-statements. You are not allowed if-statements in this lab. Instead, you must use and as helper methods to implement .


In the case of , you will find the exchange table useful in determining correct answers for your test cases. While it is not okay to use the table in itself, it is okay to use the table to decide on some test cases.

You may also use the table to craft some test cases for the function . However, you will probably find it easier to use a currency query URL to look up the correct answer, and then paste the answer into your test case.

A bigger issue with testing is that problem that we saw in class: real numbers cannot be represented exactly, which can lead to problems when trying to test equality. To solve this problem, provides a function called , which you encountered in lecture and lab. You should use this function to test instead of .

Finally, bear in mind that, like , these functions connect to the web service, and so are not instantaneous. In our solution, with complete test procedures for everything, it can take up to 10 seconds to run the unit test on campus. This will be a bit slower if you are working closer to the deadline. Again, we recommend that you run the unit test from the command shell, and via not the "run button" in Komodo Edit, so that you can see print statements as they are executed.

Finishing the Assignment

Once you have everything working, go back and make sure that your program meets the class coding conventions, including the following:

  1. There are no tabs in the file, only spaces
  2. Functions are each separated by two blank lines.
  3. Lines are short enough that horizontal scrolling is not necessary (about 80 chars is long enough). Note: if you have a long string expression of the form string1 + string2 + ... + stringk, if you put it in parentheses, you can “hit return” within the parentheses (as long as you don't do that within a string); that way your lines of code won't be over the length limit.
  4. The specifications for all of the functions, including the test procedures, are complete.
  5. Function specifications are immediately after the function header and indented.
  6. Your name(s) and netid(s) are in the comments at the top of the modules.

Turning it In

Upload the files and to CMS by the due date: Monday, February 18th at 11:59 pm. Do not submit any files with the extension/suffix . It will help to set the preferences in your operating system so that extensions always appear.

Check the CMS daily until you get feedback from a grader. Make sure your CMS notifications for CS 1110 are set so that you are sent an email when one of your grades is changed. To find the feedback, click on the Assignment 1 link in CMS; on the page you're brought to, click on the red word “show” in the line “Grading Comments & Requests (show)&rqduo; You can contact your grader if you have questions about their feedback; you can see their netid in the place you can see their feedback.

Within 24 hours of getting feedback, do RRRRR: Read the feedback, Revise your program accordingly, Resubmit, and Request a Regrade using the CMS. If you do not request a regrade, we have no simple way of knowing that you have resubmitted.

This whole process, starting from first submission onMonday, February 18th, continues until you submit a solution that demonstrates complete mastery; in some cases this may require multiple additional resubmits by you. You need to have submitted a final, correct version by Monday, February 25th, which means you'll probably want to have re-submitted at least once before then.

Appendix: Connecting to Google

This section is not part of the assignment. It is optional. Furthermore, do not make the changes suggested in this section to the files that you submit for grading; if you do, they will be sent back to you to fix.

So far you have worked with a simulated currency exchange service. But with a few changes you can use the real thing. In the web service instructions we told you to use the URL prefix If you change that prefix to you will use Google's calculator instead. And, our server has a slightly different output format: Google uses “lhs” and “rhs” instead of money_in and money_out. You should be able to modify your code to handle the Google calculator's output format. Do so, and you can try it out on converting dollars to Euros (pick small values for now).

Run the exchange function four or five times. See the value change? That is one of the reasons we did not use Google; that is too hard to test against. In fact, even employees at Google would do what we did: write a program against an unchanging exchange service before deploying it against the real thing.

There is another reason why we are not using Google. Run to get the JSON string for 1000 U.S. dollars to IDR (Indonesian rupiahs). Though the numbers may be different, the rhs value of JSON string will look something roughly like this:

rhs: "9.52380952 million Indonesian rupiahs" Once a value gets above a million, Google "anglicizes" the number, spelling out the word "million". There is a space before "million", and so your function will give the exchange rate as 9.52380952. This is incorrect, but it is not your fault; Google violated the precondition of your functions.

Solving this second problem is beyond the scope of this assignment. We will revisit it later in the course when we have the right tools. Right now, the best that you can do is implement a new version of exchange with the following specification:

def google_exchange(amount_from, currency_from, currency_to): """Returns: description of currency received in exchange. This function connects to the Google currency converter to change <amount_from> money in currency <currency_from> to the currency <currency_to>. The value returned is string with the amount and name of the new currency. Example: "9.52380952 million Indonesian rupiahs" Precondition: <amount_from> is a float. Both <currency_from> and <currency_to> are strings with valid three-letter currency codes."""

Feel free to implement this function if you wish. We will not grade it, and you will not get credit for it.

Beware the Mass Assignment

Mass assignment vulnerabilities can be a hard-to-find issue in your applications. Learn how to prevent them.
Published 2013-02-04    #vulnerability#massassignment#model

There's been a lot of talk lately about issues with the Ruby on Rails framework and some of the security issues that have come to light. There's been issues with a "magic" security value and YAML parsing that allows for code injection. What I want to focus on, however, is something that came up a while back but that PHP (code built with it, not the language itself) could be vulnerable to - a mass assignment vulnerability.

Since Rails is the framework for the Ruby world, the impact of the issue was a bit larger than it would be in the PHP community. That doesn't make it any less of a threat, though. It can also be one of those difficult to track down problems as it relies on the contents of a POST request, something usually not logged by most web servers. This issue is, like most PHP vulnerabilities unfortunately, due to bad coding practices and not handling the user inputted data correctly.

The good news is that, unless you're using something like an ORM or a model layer, you might not have to worry about this issue. It's good to keep in mind, though, given the prevalence of PHP frameworks on the web.

The Problem in PHP

So, what's this "mass assignment" problem really all about? Well, it's probably easiest to illustrate with a code example:

If you've got a keen eye, you've probably already spotted the issue with the code above. Still looking? Well, here's a hint: a little filtering could take care of the problem. Still stuck? Okay, so here's the issue - notice how there's a property on our Model above called "admin". Well, in our method we check to see if the user has this value set to .

This is all well and good, but because of how the values are assigned through the method, this could lead to dangerous consequences. So, to set the stage a bit, say that you have a form that allows for the creation of a new user (a Registration form maybe). Obviously, you're not going to have a field for in the form as you don't want to make it too easy for someone to make themselves an admin. That's the problem, though - there's no filtering on the input from the user (at least in this code) that would prevent them from submitting a value for the parameter and having that pass through to the model and get set along with the rest of the data.

This is where the crux of the "mass assignment" vulnerability lies. Due to improper filtering of the input (or access control to properties) a malicious visitor to your site could include all sorts of values in the POST request to your site, hoping to score a direct hit on the right one. This can lead to one of the OWASP Top 10, A3: Broken Authentication and Session Management and can be quite dangerous for your application and its users.

A quick word of caution for those using a framework out there that includes it's own model functionality (usually just the full-stack ones) - be sure to check and be sure that you're not leaving yourself open to this kind of attack and not even knowing it. Be sure to review the code for your framework of choice (or library) before you fully trust it.


So, we've looked at the issue - lets take a look at some of the things you can do to help mitigate the problem. Most of these are pretty simple, but some depend on the structure of your application, so keep that in mind:

  1. Filter, Filter, Filter: Yes, this has been said numerous times in other articles on this site, but this might be a little different kind of filtering than you're thinking. When you think of filtering and security, you're thinking about removing the harmful things from input that could cause problems. In this case, you want to filter out the data itself. When a user submits their POST request with the array of data, force a removal of any of the "restricted" values you don't want them to be able to override. For example, if your key is , then you'd want to call an unset on that value before calling .

  2. Enforce "restricted properties" in the Model: This is something that not a lot of frameworks or domain model libraries out there have as a feature, but it's not overly difficult to implement. Basically, what you want is a set of "restricted" items that can't be overwritten when calling something like to load the data. In our case, fits the bill. You could have a class variable that contained the list and use an in_array check to see if it's there. If it is, bypass the setting of that value.

  3. Don't make it a property: This is the one that depends on the architecture of your application. The idea here is to create your application so that things like administrative rights aren't controlled by a single property on the object. If you use something like a permission set or some other kind of flag (maybe a record in another table) it makes this kind of attack a lot less plausible. Then you could have isAdmin checks on your user reach out to this other data and evaluate from there.

Obviously, these are just a few solutions to the problem - chances are yours will differ based on how your application is structured, but this gives you a place to start.

Don't forget the type

One other thing to keep in mind that's at play here and could be tricky if you're not looking for it - see that method in the first code example? Take a look at how it's evaluating to see if the user is an admin. If you've been dealing with PHP for any length of time, you've probably come across the difference between the "equals" and how they behave. Here's a quick overview:

=Single EqualsAssign one value to another
==Double EqualsEvaluate if two values are equal
===Triple EqualsEvaluate if two values are equal and are same type

Looking at this table, do you see the problem with the first example? (hint: it's been fixed in the second code example). Since the double equals checks to see if the values are the same but does not check type, it leaves the validation open to potential abuse. PHP is a weakly typed language and allows for the shifting of one variable type to another. Without type checking, you get interesting things like , or the fun one . Unless you include that extra "equals" in there, your check is not valid and could cause pain for you down the line.

As a general rule, evaluation in PHP should be as specific as possible. Use triple equals and the ctype_* methods to ensure your data is what you're expecting. When in doubt, filter it out (don't try to adjust).

About PHP Frameworks

I went back and looked through the model/ORM layers of some of the major frameworks in use today to see if they allowed the concept of "protected properties" in their code, but found almost nothing about it. Most of the frameworks (including CakePHP and FuelPHP). I was, however, happy to see that the upcoming version of the Laravel Framework (version 4) has something included to deal with the mass assignment issue via a and property lists:

The property names are a little odd, but they get the point across. It's good to see some concern for this in the PHP community. Hopefully some of the other frameworks with their own ORMs will follow suit.


by Chris Cornutt

With over 12 years of experience in development and a focus on application security Chris is on a quest to bring his knowledge to the masses, making application security accessible to everyone. He also is an avodcate for security in the PHP community and provides application security training and consulting services.


Leave a Reply

Your email address will not be published. Required fields are marked *